Aller au contenu

#Filtres

Un filtre transforme une valeur avant de l’écrire : {{ x | filtre: argument }}. Webcosa en ajoute neuf aux filtres standard de Liquid, et ces neuf-là sont ceux qu’un thème de site vitrine utilise vraiment. Cette page donne, pour chacun, sa signature, ce qu’il fait, une entrée et la sortie exacte.

Les sorties ci-dessous ont été obtenues en exécutant le moteur, pas en le lisant. La source est la fonction ajouterFiltres() de src/lib/liquid/moteur.ts : si un filtre y est ajouté ou retiré, c’est elle qui fait foi, pas cette page.

#Les filtres maison

FiltreEntrée → sortie
asset_url'theme.css' → /theme-assets/8f2c…/theme.css
stylesheet_tagune adresse → une balise link complète
teinte: n'#3366cc' | teinte: 40 → #85a3e0
font_face'inter' → la règle @font-face de la police
font_famille'inter' → "Inter Var", <repli système>
lignesun texte multiligne → le même, avec des br
date_fr'2026-03-08T10:00:00Z' → 8 mars 2026
handle'Prêt-à-Porter n°3' → pret-a-porter-n-3
escapea<b&c → a&lt;b&amp;c

#asset_url

Fabrique l’adresse servie d’un fichier d’assets/. Il prend le nom seul du fichier — jamais son chemin.

entréechaînerequis

Le nom du fichier tel qu’il apparaît dans assets/, extension comprise, sans le dossier.

Entrée et sortie
{{ 'theme.css' | asset_url }}
→ /theme-assets/8f2c1b40-…/theme.css
Danger

Le segment assets/ n’apparaît PAS dans l’adresse produite. La route est /theme-assets/<id>/<fichier> — un seul segment de nom — et c’est elle qui remet le préfixe assets/ pour interroger la base.

Écrire {{ '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 : du HTML nu, sans style. C’est exactement le bug qui a tenu, sur tous les sites à thème Liquid à la fois, sans jamais lever d’erreur. Un thème sans CSS ressemble à un thème mal écrit, pas à une adresse fausse — voilà pourquoi personne ne l’a vu.

La forme correcte, et la seule : le nom du fichier, rien d’autre.

Le filtre lit theme_base dans le contexte. Hors du rendu d’un site — dans un essai isolé, par exemple — theme_base est absent et la sortie tombe à /theme.css. Ce n’est pas une erreur du filtre, c’est un contexte incomplet.

#stylesheet_tag

Enveloppe une adresse dans une balise link. Il s’enchaîne avec asset_url, et c’est ainsi que les neuf thèmes livrés chargent leur feuille de style.

layout/theme.liquidliquid
{{ 'theme.css' | asset_url | stylesheet_tag }}
Sortiehtml
<link rel="stylesheet" href="/theme-assets/8f2c1b40-…/theme.css">

L’adresse est échappée au passage. Le filtre ne prend aucun argument : ni media, ni preload. Si tu as besoin d’autre chose, écris la balise à la main autour de asset_url.

#teinte

Éclaircit ou assombrit une couleur hexadécimale.

entréecouleur hexadécimale à six chiffresrequis

#3366cc ou 3366cc. Les formes courtes (#36c), les noms de couleur et les notations rgb() ou oklch() ne sont pas reconnues : la valeur est alors rendue inchangée.

deltanombre entre -100 et 100défaut : 0

Positif, la couleur est éclaircie — elle se rapproche du blanc. Négatif, elle est assombrie — elle se rapproche du noir. La valeur est un pourcentage du chemin restant : teinte: 100 rend du blanc pur, teinte: -100 du noir pur.

Entrée et sortieliquid
{{ '#3366cc' | teinte: 40 }}    → #85a3e0
{{ '#3366cc' | teinte: -40 }}   → #1f3d7a
{{ '#3366cc' | teinte: 0 }}     → #3366cc
{{ 'bleu'    | teinte: 40 }}    → bleu

Ce filtre est la raison pour laquelle un panneau de réglages Webcosa tient en cinq couleurs au lieu de vingt. Sans lui, chaque nuance d’une couleur réglable — le survol d’un bouton, le fond d’une carte, la bordure d’un champ — devrait être un réglage de plus. Vingt sélecteurs de couleur dans un panneau, personne ne les remplit : le client en règle trois, laisse les autres à leur défaut, et obtient un site incohérent qu’il croit mal conçu.

layout/theme.liquid — Origo, extraitliquid
:root {
  --accent: {{ settings.accent }};
  --accent-sombre: {{ settings.accent | teinte: -18 }};
  --accent-clair: {{ settings.accent | teinte: 88 }};
}

Un seul réglage de couleur, trois variables CSS, et toute la palette d’accent du site suit quand le client change d’avis.

#font_face

Écrit la règle @font-face d’une police du catalogue. Un thème ne transporte pas de police, il en nomme une : assets/ n’accepte que .css et .svg, aucun fichier de police ne peut donc y vivre.

Entrée et sortieliquid
{{ 'inter' | font_face }}
→ @font-face{font-family:"Inter Var";src:url("/polices/inter.woff2?v=2") format("woff2");font-weight:100 900;font-style:normal;font-display:swap;}

Un identifiant inconnu rend du vide, et c’est voulu. La valeur ne sert qu’à chercher dans un catalogue fermé, écrit par nous : interpoler directement un réglage choisi par le client dans une feuille de style servie à tous ses visiteurs serait une injection.

#font_famille

Rend la pile CSS complète : la famille, puis son repli système.

Entrée et sortieliquid
{{ 'inter' | font_famille }}
→ "Inter Var", -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif

Le repli n’est pas décoratif : le fichier arrive après le premier rendu, et font-display: swap affiche le texte dans le repli en attendant. Comme font_face, il rend du vide pour un identifiant inconnu — d’où le test != blank des coquilles, qui retombent alors sur la pile système. Chez Origo, c’est le texte qui la porte en toutes lettres ; les titres, eux, repassent par la pile du texte.

layout/theme.liquid — Origo, extraitliquid
<style>
  {{ settings.police_titres | font_face }}
</style>

<style>
  {%- assign pile_titres = settings.police_titres | font_famille -%}
  {%- assign pile_texte = settings.police_texte | font_famille -%}
  :root {
    --texte-police: {% if pile_texte != blank %}{{ pile_texte }}{% else %}ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif{% endif %};
    --titre-police: {% if pile_titres != blank %}{{ pile_titres }}{% else %}var(--texte-police){% endif %};
  }
</style>

Sept des neuf thèmes livrés appellent ces deux filtres dans leur coquille : Cadence, Fondant, Forge, Origo, Piazza, Scène et Vela. Le détail du réglage qui les alimente est sur la page des réglages.

#lignes

Transforme les sauts de ligne en balises br, après avoir échappé le HTML. C’est le filtre à appliquer à tout texte multiligne saisi par le client : une adresse postale, des horaires.

Entrée et sortie
entrée   12 rue des Lilas ⏎ 75011 Paris
sortie   12 rue des Lilas<br>75011 Paris

entrée   a<b ⏎ c
sortie   a&lt;b<br>c
sections/contact.liquid — Origoliquid
{%- if section.settings.adresse != blank -%}
  <div><dt>Adresse</dt><dd>{{ section.settings.adresse | lignes }}</dd></div>
{%- endif -%}

L’ordre compte : l’échappement a lieu avant l’insertion des br, donc les balises produites survivent et le contenu saisi ne peut pas en fabriquer. Un client qui tape <b>gras</b> dans le champ Adresse verra les chevrons, pas du gras — c’est le comportement voulu pour un champ textarea. Pour du texte formaté, c’est un réglage richtext qu’il faut déclarer.

#date_fr

Formate une date en français, sans dépendance.

Entrée et sortieliquid
{{ '2026-03-08T10:00:00Z' | date_fr }}   → 8 mars 2026
{{ '' | date_fr }}                       → (rien)
{{ 'pas une date' | date_fr }}           → (rien)

Le style est long (8 mars 2026), le fuseau est Europe/Paris, la langue est fr-FR — les trois sont fixes et ne se règlent pas. Une entrée illisible rend une chaîne vide plutôt qu’Invalid Date : mieux vaut une ligne manquante qu’un message d’erreur en anglais dans le pied d’une carte d’article.

#handle

Réduit un texte à un identifiant utilisable en ancre ou en classe CSS : minuscules, accents retirés, tout le reste remplacé par des tirets, tirets de bord supprimés.

Entrée et sortieliquid
{{ 'Prêt-à-Porter n°3'        | handle }}  → pret-a-porter-n-3
{{ 'Élagage & Taille — 100 %' | handle }}  → elagage-taille-100
Un titre de section devient son ancreliquid
<h2 id="{{ section.settings.titre | handle }}">{{ section.settings.titre }}</h2>

Deux thèmes livrés s’en servent — Aplomb et Scène — pour fabriquer un identifiant qui suit un contenu variable : l’ancre d’un bloc, la clé d’un groupe de FAQ. Les autres écrivent leurs ancres en dur (id="contact", id="journal"), ce qui est plus sûr quand l’ancre doit rester stable même si le client change le titre.

#escape

Échappe le HTML. Explicite, comme chez Shopify : Liquid n’échappe rien par défaut.

Entrée et sortieliquid
{{ '<b>x</b>' | escape }}   → &lt;b&gt;x&lt;/b&gt;

Il échappe cinq caractères : &, <, >, " et ' — cette dernière en &#39;, comme le fait LiquidJS. Un attribut délimité par des apostrophes est donc protégé au même titre qu’un attribut à guillemets doubles.

Astuce

Prends quand même l’habitude des guillemets doubles pour tous tes attributs : c’est la convention des thèmes livrés, et un attribut à guillemets doubles reste lisible quand le contenu porte une apostrophe française.

Où l’appliquer, concrètement : sur tout contenu saisi par le client qui atterrit dans un attribut — un alt, un title, un value. Dans le corps du document, un thème qui vient de nous ou du propriétaire du site peut se passer d’échapper ; c’est une décision à revoir le jour où des thèmes de tiers circuleront. Voir le bac à sable.

snippets/image.liquid — Origoliquid
<img
  src="{{ src }}"
  alt="{{ alt | default: '' | escape }}"
  loading="{{ loading | default: 'lazy' }}"
  decoding="async">

#Les filtres de Liquid

Tous les filtres standard de LiquidJS sont disponibles. Voici ceux que les thèmes livrés utilisent réellement — c’est un bon indicateur de ce dont un thème de site vitrine a besoin.

FiltreCe qu’il faitVu dans
default: vremplace une valeur vide, nulle ou faussepartout, 589 fois
replace: a, bremplace toutes les occurrencestel: sans espaces
split: sdécoupe une chaîne en tableaulistes saisies en une ligne
slice: i, nextrait une portioninitiales d’un nom
prepend: scolle devantpréfixes d’adresse
plus: nadditionnenumérotation
sections/entete.liquid — Origoliquid
<a class="entete-tel" href="tel:{{ section.settings.telephone | replace: ' ', '' }}">
  {{ section.settings.telephone }}
</a>

Le numéro est affiché tel qu’il a été saisi, espaces compris, et le lien tel: en est débarrassé. Deux usages de la même valeur, une seule saisie.

Le reste du catalogue standard répond aussi : upcase, downcase, capitalize, strip, truncate, truncatewords, size, first, last, join, sort, uniq, map, where, reverse, date, round, ceil, floor, minus, times, divided_by, modulo, newline_to_br, strip_html, json, slugify… La liste complète est chez LiquidJS, et elle fait autorité pour tout ce que cette page ne redit pas.

Note

escape est le seul filtre standard que Webcosa remplace. Tous les autres se comportent comme LiquidJS les décrit.

#Un filtre inconnu ne lève pas d’erreur

Le moteur tourne avec strictFilters désactivé. Un filtre qui n’existe pas rend la valeur inchangée, sans un mot.

Trois filtres Shopify qui n’existent pas iciliquid
{{ 'accueil'  | t }}                    → accueil
{{ image      | img_url: '400x' }}      → (la valeur d'origine)
{{ 1000       | money }}                → 1000

C’est le bon comportement en production — une faute de frappe ne doit pas éteindre une page — et c’est le piège classique quand on transpose un thème Shopify : le code paraît fonctionner, la page rend, et les valeurs traversent les filtres sans être transformées. Si un montant s’affiche 1000 au lieu de 10,00 €, ce n’est pas que le filtre a échoué, c’est qu’il n’existe pas. La liste de ce qui manque et pourquoi est sur la page des absents.