Aller au contenu principal
Version: 26.0.9.0

Créer un blog avec portfolio

Ce tutoriel vous guide de A à Z dans la création d'un blog avec section portfolio en utilisant Elementum.

Projet fil rouge : le thème my-blog, un blog avec :

  • Page d'accueil personnalisée
  • Liste et détail des articles
  • Archives par catégorie
  • Section Portfolio (CPT project + taxonomie technology)
  • Couleurs personnalisables via le Customizer

Prérequis : Elementum installé et activé, WordPress fonctionnel, Node.js disponible.

Installer et activer Elementum
  1. Clonez le plugin dans wp-content/plugins/ :
    git clone https://github.com/devopress/elementum wp-content/plugins/elementum
  2. Activez-le dans Extensions de l'administration WordPress, ou via WP-CLI :
    wp plugin activate elementum
  3. Une fois activé, Elementum expose une interface d'administration à l'adresse /elementum/admin — vous pouvez l'utiliser à la place de /wp-admin ou /wp-login.php pour vous connecter et gérer votre site.

Étape 1 — Mise en place

Créer le thème avec Dobby

Elementum dispose d'un outil CLI appelé Dobby qui automatise la création de projets. Plutôt que de créer manuellement les dizaines de fichiers nécessaires à un thème WordPress (autoloading, structure MVC, configuration Vite, etc.), une seule commande suffit :

php dobby new:theme my-blog
Dobby génère toute la base du thème automatiquement
my-blog/
app/
Controller/
ThemeController.php ← contrôleur principal (généré par Dobby)
PostTypes/ ← dossier pour vos types de contenu (vide)
assets/
index.ts ← point d'entrée JS front-end
admin.ts ← point d'entrée JS admin
customize.ts ← point d'entrée JS Customizer
scss/
main.scss ← point d'entrée CSS
customize/ ← dossier pour vos fichiers YAML (vide)
views/
base.html.twig ← layout HTML de base
front-page.html.twig ← template page d'accueil
composer.json ← autoloading PSR-4
vite.config.js ← configuration du build
style.css ← en-tête WordPress obligatoire

La plupart de ces fichiers sont déjà fonctionnels tels quels. Vous n'aurez qu'à les compléter, pas les créer.

Installer les dépendances et démarrer le build

Les assets (SCSS, TypeScript) sont compilés par Vite. Pour démarrer en mode développement avec rechargement automatique :

cd wp-content/themes/my-blog
composer dump-autoload # pour initialiser l'autoloader PSR-4
npm install
npm run dev

npm run dev surveille vos fichiers en temps réel : chaque modification dans assets/ déclenche une recompilation instantanée. Les fichiers compilés apparaissent dans assets/dist/.

astuce

Activez le thème dans Apparence > Thèmes de l'administration WordPress avant de continuer.

Activer Tailwind CSS

Pour styliser nos templates sans écrire de SCSS from scratch, on va activer Tailwind CSS v4 via le CDN. Elementum lit un fichier config/app.yaml dans lequel vous pouvez surcharger sa configuration. Plutôt que de le créer manuellement, utilisez la commande publish pour copier la configuration d'Elementum comme point de départ :

php dobby publish

Dobby propose un menu interactif listant tous les fichiers de configuration disponibles dans l'écosystème. Sélectionnez elementum - app. Le fichier est copié dans wp-content/config/app.yaml.

Ouvrez-le et passez tailwind.front à true :

wp-content/config/app.yaml
# Copié depuis elementum/config/app.yaml

assets:
    tailwind:
        front: true # ← activer ici
        admin: false
        customize: false

Elementum fusionne ce fichier avec le sien : en mettant front: true, il injecte automatiquement le CDN @tailwindcss/browser@4 côté front-end — aucune installation npm supplémentaire.

Tailwind v4 en mode CDN scanne les classes HTML en temps réel : vous pouvez utiliser class="..." directement dans vos templates Twig.

attention

Le CDN est idéal en développement. Pour la production, préférez une compilation via PostCSS dans vite.config.js.


Étape 2 — Le layout de base

Pourquoi un layout de base ?

Dans un thème WordPress classique, vous répétez le <head>, le header et le footer dans chaque fichier (header.php, footer.php, etc.). Elementum utilise Twig avec le système d'héritage de templates : un seul fichier base.html.twig contient la structure commune, et chaque page l'étend en ne redéfinissant que son contenu propre.

Le fichier base.html.twig

Dobby ne génère ce fichier dans views/components/layouts/.

Voici une version enrichie pour notre blog :

views/components/layouts/base.html.twig
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    {% if title is defined %}
        <title>{{ title }}</title>
    {% endif %}
    {{ fn('wp_head') }}
</head>
<body class="{{ fn('body_class') }} min-h-screen bg-gray-50 text-gray-800 flex flex-col">

    <header class="bg-white border-b border-gray-200 sticky top-0 z-10">
        <div class="max-w-5xl mx-auto px-4 py-3 flex items-center justify-between">
            <a href="{{ site.url }}" class="text-xl font-bold text-indigo-600 hover:text-indigo-800">
                {{ site.name }}
            </a>
            {% menu 'header' %}
                <nav class="flex gap-4">
                    {% for item in menu %}
                        <a href="{{ item.url }}"
                           class="text-sm font-medium {{ item.current ? 'text-indigo-600' : 'text-gray-600 hover:text-indigo-600' }}">
                            {{ item.title }}
                        </a>
                    {% endfor %}
                </nav>
            {% endmenu %}
        </div>
    </header>

    <main class="flex-1 max-w-5xl mx-auto w-full px-4 py-10">
        {% block content %}{% endblock %}
    </main>

    <footer class="bg-white border-t border-gray-200 text-center text-sm text-gray-400 py-6">
        <p>© {{ site.name }} {{ 'now'|date('Y') }}</p>
    </footer>

    {{ fn('wp_footer') }}
</body>
</html>

Quelques points importants :

  • fn('wp_head') et fn('wp_footer') sont obligatoires — WordPress y injecte les scripts et styles des plugins. Oubliez-les et la moitié des plugins cesse de fonctionner.
  • fn('body_class') génère les classes CSS contextuelles de WordPress (ex : single-post, page-template-default…), utiles pour cibler des pages en CSS.
  • site.name / site.url sont des variables globales qu'Elementum injecte automatiquement dans tous vos templates Twig.
  • {% menu 'header' %} est un tag Twig Elementum qui charge le menu WordPress nommé header. La variable menu est alors disponible dans la boucle. Les menus sont défini dans le fichier de config menu.yaml.
  • {% block content %} est le point d'injection : chaque template enfant remplace ce bloc par son propre contenu.
Pour aller plus loin

Variables globales Twigsite, page, user, request.
Tag {% menu %} — rendu des menus WordPress.

Vous pouvez {{ dump(_context) }} pour voir toutes les variables disponibles dans le contexte Twig.


Étape 3 — Page d'accueil & liste d'articles

Comment Elementum gère les templates ?

Dans WordPress vanilla, la hiérarchie des templates est gérée par des fichiers PHP (front-page.php, home.php, single.php…). Avec Elementum, ces fichiers n'existent plus. À la place, vous déclarez des méthodes dans un contrôleur PHP et vous leur attachez un attribut #[Template] qui indique à quel type de page elles correspondent.

Cela centralise toute la logique métier dans un seul endroit et sépare proprement le PHP du HTML (Twig).

Le contrôleur — fichier généré par Dobby

Le fichier app/Controller/ThemeController.php est déjà créé par Dobby avec une méthode exemple. Remplacez son contenu par :

app/Controller/ThemeController.php
<?php

use Elementum\Attributes\Template;
use Elementum\Attributes\Action;
use Elementum\Abstract\Controller;

class ThemeController extends Controller
{
    #[Template('front_page')]
    public function frontPage()
    {
        // On récupère les 3 derniers articles pour les afficher sur la home.
        // getPostType('post') donne accès au type natif WordPress "post".
        $latestPosts = $this->getPostType('post')->getQuery(['posts_per_page' => 3]);

        return $this->render('@theme/front-page.html.twig', [
            'latestPosts' => $latestPosts,
        ]);
    }

    #[Template('home')]
    public function blogIndex()
    {
        // Pas besoin de passer la requête : WordPress la gère tout seul
        // sur la page "Articles" définie dans Réglages > Lecture.
        return $this->render('@theme/home.html.twig');
    }

    #[Template('single')]
    public function singlePost()
    {
        // La variable $post est automatiquement disponible dans le template
        // via la variable globale Twig `post`. Pas besoin de la passer manuellement.
        return $this->render('@theme/single.html.twig');
    }
}

Quelques précisions sur le rôle de chaque template :

  • front_page — page d'accueil statique, configurée dans Réglages > Lecture ("Une page statique"). C'est ici qu'on met le hero, les derniers articles en avant, etc.
  • home — page listant tous les articles, également définie dans Réglages > Lecture ("Page des articles"). Elementum la configure automatiquement ; elle se comporte comme une archive paginée de posts natifs. Pas besoin d'un template archive séparé pour les posts.
  • single — article individuel, affiché quand on clique sur un article.

La page d'accueil — fichier généré par Dobby, à renommer

Dobby crée un index.html.twig minimal. Renommez-le en front-page.html.twig :

views/front-page.html.twig
{% extends '@theme/components/layouts/base.html.twig' %}

{% set title = 'Accueil | ' ~ site.name %}

{% block content %}
    <section class="text-center py-20 bg-indigo-600 text-white rounded-2xl mb-12">
        <h1 class="text-4xl font-bold mb-4">Bienvenue sur mon blog</h1>
        <p class="text-indigo-100 text-lg mb-8">Développeur passionné, je partage ici mes articles et projets.</p>
        <a href="{{ fn('get_permalink', fn('get_option', 'page_for_posts')) }}"
           class="inline-block bg-white text-indigo-600 font-semibold px-6 py-3 rounded-full hover:bg-indigo-50 transition">
            Voir les articles
        </a>
    </section>

    <section>
        <h2 class="text-2xl font-bold mb-6">Derniers articles</h2>
        <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
            {% wp_query latestPosts %}
                {{ component('card/article', { post: post }) }}
            {% endwp_query %}
        </div>
    </section>
{% endblock %}

{% wp_query latestPosts %} itère sur la WP_Query passée depuis le contrôleur. À chaque itération, la variable post contient l'article courant. On délègue son rendu au composant card/article.

Composant card/article — fichier à créer manuellement

Les composants Twig sont des fragments réutilisables. Contrairement aux templates, Dobby ne les génère pas — vous les créez selon vos besoins dans views/components/.

Créez le fichier views/components/card/article.html.twig :

views/components/card/article.html.twig
<article class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition">
    {% if post.thumbnail %}
        <img src="{{ post.thumbnail.src }}" alt="{{ post.thumbnail.alt }}"
             class="w-full h-48 object-cover">
    {% endif %}
    <div class="p-5">
        <h3 class="text-lg font-semibold mb-2">
            <a href="{{ post.link }}" class="hover:text-indigo-600">{{ post.title }}</a>
        </h3>
        <p class="text-gray-500 text-sm mb-4">{{ post.get_preview(20, true) }}</p>
        <a href="{{ post.link }}"
           class="text-sm font-medium text-indigo-600 hover:underline">Lire la suite →</a>
    </div>
</article>

Ce composant attend un paramètre post et en affiche les données. On l'appelle depuis n'importe quel template avec :

{{ component('card/article', { post: post }) }}

L'intérêt : vous définissez la carte article une seule fois, et vous la réutilisez sur l'accueil, la liste des articles, les archives…

La liste des articles — fichier à créer manuellement

Créez views/home.html.twig :

views/home.html.twig
{% extends '@theme/components/layouts/base.html.twig' %}

{% set title = 'Blog | ' ~ site.name %}

{% block content %}
    <h1 class="text-3xl font-bold mb-8">Tous les articles</h1>

    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {% wp_query %}
            {{ component('card/article', { post: post }) }}
        {% endwp_query %}
    </div>

    <div class="mt-10 flex justify-center">
        {{ fn('the_posts_pagination') }}
    </div>
{% endblock %}

Ici {% wp_query %} est utilisé sans argument : il utilise automatiquement la requête WordPress courante, celle que WordPress a construite pour la page des articles. Pas besoin de la re-créer dans le contrôleur.

Pour aller plus loin

Attribut #[Template] — liste complète des types de templates.
Fonction component() — paramètres et surcharge.
Tag {% wp_query %} — boucles WordPress dans Twig.


Étape 4 — Article single

Fichier à créer manuellement

Créez views/single.html.twig :

views/single.html.twig
{% extends '@theme/components/layouts/base.html.twig' %}

{% set title = post.title ~ ' | ' ~ site.name %}

{% block content %}
    <article class="max-w-2xl mx-auto">
        <header class="mb-8">
            <h1 class="text-4xl font-bold mb-3">{{ post.title }}</h1>
            <p class="text-sm text-gray-400">
                Publié le {{ post.date|date('d/m/Y') }} dans
                {% for cat in post.get_terms('category') %}
                    <span class="text-indigo-500">{{ cat.name }}</span>
                {% endfor %}
            </p>
        </header>

        {% if post.thumbnail %}
            <img src="{{ post.thumbnail.src }}" alt="{{ post.thumbnail.alt }}"
                 class="w-full rounded-xl mb-8 object-cover max-h-80">
        {% endif %}

        <div class="prose prose-indigo max-w-none">
            {{ post.content }}
        </div>
    </article>
{% endblock %}

La variable globale post est automatiquement disponible dans tous les templates de type single. Elementum l'injecte via le service Twig global — vous n'avez pas à la passer depuis le contrôleur.

info

post est une instance de la façace Post.

Pour aller plus loin

Variables globales page et post — accès aux données courantes en Twig.


Étape 5 — CPT Projets avec taxonomie

Pourquoi un Custom Post Type ?

WordPress gère nativement les articles (post) et les pages (page). Pour stocker des projets de portfolio, vous pourriez les mettre dans des articles, mais ce serait mélanger des types de contenu très différents. Un Custom Post Type (CPT) dédié vous donne :

  • Une entrée séparée dans le menu d'administration
  • Des URLs propres (/project/mon-projet/)
  • Une page d'archive dédiée (/project/)
  • La possibilité d'y associer des taxonomies spécifiques

Créer le CPT avec Dobby

php dobby make:post Project

Dobby génère app/PostTypes/Project.php avec la structure de base. Modifiez-le pour y ajouter nos options :

app/PostTypes/Project.php
<?php

use Elementum\Abstract\PostType;

class Project extends PostType
{
    const string SLUG = 'project';

    public function __construct()
    {
        $this->slug     = self::SLUG;
        $this->menuName = 'Projets';
        $this->labels   = [
            'plural'   => 'projets',
            'singular' => 'projet',
        ];
        $this->isMale = true;
        $this->icon   = 'dashicons-portfolio';

        // On active : titre, éditeur, image mise en avant, extrait.
        // has_archive: true génère automatiquement une page /project/.
        $this->setSupports('title', 'editor', 'thumbnail', 'excerpt')
             ->setOption('has_archive', true)
             ->setOption('menu_position', 5);

        // Personnalisation des colonnes dans la liste admin :
        // on supprime "Date" et on la rajoute en fin, avec une colonne "Technologies" avant.
        $this->columns = [
            'remove' => ['date'],
            'add'    => [
                'project_tech' => 'Technologies',
                'date'         => 'Date',
            ],
        ];

        $this->setTitlePlaceholder('Nom du projet');

        // Toujours appeler en dernier — enregistre le CPT dans WordPress.
        parent::__construct();
    }

    // Elementum appelle automatiquement cette méthode si elle existe.
    // C'est ici qu'on déclare les taxonomies liées au CPT.
    public function taxonomies(): void
    {
        $this->addTaxonomy('technology', 'Technologies')
            ->isFemale()
            ->setLabels([
                'plural'   => 'technologies',
                'singular' => 'technologie',
            ])
            ->register();
    }
}

Dès que vous enregistrez ce fichier, le CPT project et la taxonomie technology apparaissent dans l'administration WordPress — sans configuration supplémentaire.

Templates du portfolio — méthodes à ajouter dans le contrôleur

Le CPT project a ses propres types de pages : une archive et un single. Pour y associer des templates distincts, on utilise le paramètre params de #[Template].

Ajoutez ces deux méthodes dans ThemeController.php :

app/Controller/ThemeController.php — ajouts
#[Template('post_type_archive', params: ['project'])]
public function portfolioArchive()
{
    // On récupère tous les projets via le service PostType.
    // getQuery() sans argument retourne une WP_Query avec les réglages par défaut.
    $projects = $this->getPostType(Project::SLUG)->getQuery();

    return $this->render('@theme/archive-project.html.twig', [
        'projects' => $projects,
    ]);
}

#[Template('singular', params: ['project'])]
public function singleProject()
{
    // params: ['project'] restreint ce template aux singles du CPT "project" uniquement.
    // Sans params, #[Template('single')] s'appliquerait à tous les singles — articles inclus.
    return $this->render('@theme/single-project.html.twig');
}

Templates Twig du portfolio — fichiers à créer manuellement

Créez views/archive-project.html.twig :

views/archive-project.html.twig
{% extends '@theme/components/layouts/base.html.twig' %}

{% set title = 'Portfolio | ' ~ site.name %}

{% block content %}
    <h1 class="text-3xl font-bold mb-8">Portfolio</h1>

    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {% wp_query projects %}
            {{ component('card/project', { post: post }) }}
        {% endwp_query %}
    </div>
{% endblock %}

Créez le composant views/components/card/project.html.twig :

views/components/card/project.html.twig
<article class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition">
    {% if post.thumbnail %}
        <img src="{{ post.thumbnail.src }}" alt="{{ post.thumbnail.alt }}"
             class="w-full h-48 object-cover">
    {% endif %}
    <div class="p-5">
        <h3 class="text-lg font-semibold mb-3">
            <a href="{{ post.link }}" class="hover:text-indigo-600">{{ post.title }}</a>
        </h3>
        {% set techs = post.get_terms('technology') %}
        {% if techs %}
            <div class="flex flex-wrap gap-2">
                {% for tech in techs %}
                    <span class="text-xs bg-indigo-50 text-indigo-600 font-medium px-2 py-1 rounded-full">
                        {{ tech.name }}
                    </span>
                {% endfor %}
            </div>
        {% endif %}
    </div>
</article>

post.get_terms('technology') récupère les termes de la taxonomie technology attachés au projet courant.

Créez views/single-project.html.twig :

views/single-project.html.twig
{% extends '@theme/components/layouts/base.html.twig' %}

{% set title = post.title ~ ' | Portfolio | ' ~ site.name %}

{% block content %}
    <article class="max-w-2xl mx-auto">
        <h1 class="text-4xl font-bold mb-4">{{ post.title }}</h1>

        {% set techs = post.get_terms('technology') %}
        {% if techs %}
            <div class="flex flex-wrap gap-2 mb-6">
                {% for tech in techs %}
                    <a href="{{ tech.link }}"
                       class="text-xs bg-indigo-50 text-indigo-600 font-medium px-2 py-1 rounded-full hover:bg-indigo-100">
                        {{ tech.name }}
                    </a>
                {% endfor %}
            </div>
        {% endif %}

        <div class="prose prose-indigo max-w-none">
            {{ post.content }}
        </div>
    </article>
{% endblock %}
Pour aller plus loin

Créer un CPT — toutes les options de configuration.
Taxonomies — labels, méthodes fluides, addTaxonomy().


Étape 6 — Customizer

Pourquoi utiliser le Customizer d'Elementum ?

Le Customizer WordPress natif demande beaucoup de code PHP répétitif pour déclarer chaque panneau, section et champ. Elementum simplifie cela : vous décrivez vos options dans un fichier YAML, et le framework se charge de tout enregistrer.

Le fichier colors.yaml — déjà présent dans le boilerplate

Dobby a déjà généré un fichier customize/colors.yaml dans votre thème. Son contenu est directement utilisable :

customize/colors.yaml
theme_colors:
    title: "Theme - Couleurs"
    description: "Couleurs du theme"
    priority: 10
    active: true

    sections:
        palette:
            title: "Palette de couleur"
            description: "Couleurs de la palette"

            fields:
                theme_primary:
                    label: "Couleur principale"
                    type: "color_contrast" # type spécial : vérifie le contraste WCAG automatiquement
                    default: "#ddffdf"
                    check_contrast_colors_ids: theme_text_primary
                    add_to_editor: true # ajoute la couleur à la palette Gutenberg

                theme_secondary:
                    label: "Couleur secondaire"
                    type: "color_contrast"
                    default: "#bef9f1"
                    check_contrast_colors_ids: theme_text_secondary
                    add_to_editor: true

                theme_tertiary:
                    label: "Couleur tertiaire"
                    type: "color_contrast"
                    default: "#b9aae9"
                    check_contrast_colors_ids: theme_text_tertiary
                    add_to_editor: true

                theme_text_primary:
                    label: "Couleur du texte principal"
                    type: "color_contrast"
                    default: "#212121"
                    check_contrast_colors_ids: theme_primary

                theme_text_secondary:
                    label: "Couleur du texte secondaire"
                    type: "color_contrast"
                    default: "#EFEFEF"
                    check_contrast_colors_ids: theme_secondary

                theme_text_tertiary:
                    label: "Couleur du texte tertiaire"
                    type: "color_contrast"
                    default: "#212121"
                    check_contrast_colors_ids: theme_tertiary

Dès l'enregistrement, les options apparaissent dans Apparence > Personnaliser.

Les IDs des champs (theme_primary, theme_text_primary…) sont intentionnellement raccord avec les variables SCSS déclarées dans assets/scss/utils/_variables.scss, déjà présent dans le boilerplate :

assets/scss/utils/_variables.scss
$theme-primary: #2d2a24 !default;
$theme-secondary: #ecd4a7 !default;
$theme-tertiary: #d69a5a !default;

$theme-text-primary: #ecd4a7 !default;
$theme-text-secondary: #212121 !default;
$theme-text-tertiary: #212121 !default;

Ces variables définissent des valeurs par défaut (!default). Elementum les surcharge automatiquement avec les valeurs saisies dans le Customizer — les noms correspondent directement (les tirets devenant des underscores dans le YAML).

Utiliser les couleurs dans le SCSS

Vous n'avez rien d'autre à faire côté PHP. Quand l'utilisateur sauvegarde le Customizer, Elementum recompile automatiquement tous les fichiers assets/scss/main.scss de l'écosystème via scssphp. Il récupère les valeurs des champs color_contrast et les injecte directement comme variables SCSS avant la compilation — en remplaçant les valeurs !default de _variables.scss.

Le CSS compilé est écrit dans uploads/elementum/style.min.css (dossier toujours accessible en écriture par le process web) et servi automatiquement en priorité sur le styles.css de assets/dist/.

Vous utilisez donc vos variables SCSS de manière tout à fait classique :

assets/scss/main.scss
@import "utils/variables"; // contient $theme-primary, $theme-text-primary…

body {
    color: $theme-text-primary;
    background-color: $theme-secondary;
}

a {
    color: $theme-primary;
}

Lors de la sauvegarde du Customizer, Elementum substitue $theme-primary par la valeur choisie par l'utilisateur et recompile

En prévisualisation

Dans l'aperçu live du Customizer (avant sauvegarde), Elementum injecte également le CSS recompilé directement dans un <style> du <head> pour afficher les changements en temps réel.

Pour aller plus loin

Le Customizer — panels, sections, priorités.
Champ color_contrast — vérification d'accessibilité automatique.


Étape 7 — SCSS & Assets

Organisation du SCSS — tout à créer manuellement

Dobby génère un assets/scss/main.scss vide. À vous de l'organiser. Voici une structure recommandée pour un projet de cette taille :

assets/scss/
main.scss ← point d'entrée, importe tout (généré par Dobby)
utils/
_variables.scss ← couleurs et espacements par défaut (fallbacks)
_breakpoints.scss ← mixins responsive
layouts/
_header.scss
_footer.scss
components/
_card-article.scss
_card-project.scss
_btn.scss
pages/
_home.scss
_portfolio.scss
assets/scss/main.scss
@import "./utils/variables";
@import "./utils/breakpoints";

@import "./layouts/header";
@import "./layouts/footer";

@import "./components/btn";
@import "./components/card-article";
@import "./components/card-project";

@import "./pages/home";
@import "./pages/portfolio";

L'alias @elementum

Vite est configuré avec un alias @elementum qui pointe vers le dossier assets/ du plugin Elementum. Cela vous donne accès aux utilitaires SCSS du framework :

@import "@elementum/scss/utils/variables";

Build pour la production

Une fois le développement terminé, compilez les assets optimisés :

npm run build

Les fichiers compilés apparaissent dans assets/dist/. Elementum les enqueue automatiquement dans WordPress via son service de gestion des assets — vous n'avez pas à écrire de wp_enqueue_scripts.

Pour aller plus loin

Assets — JS, SCSS & React — pipeline Vite complet, Alpine.js, React.


Étape 8 — TypeScript

Le boilerplate Dobby est préconfiguré avec TypeScript et Vite. TypeScript apporte le typage statique à JavaScript : les erreurs sont détectées à l'écriture plutôt qu'à l'exécution, ce qui rend le code plus robuste et plus maintenable.

Point d'entrée et structure

Dobby génère assets/index.ts, point d'entrée du bundle front-end compilé par Vite. Il importe assets/js/app.ts, votre fichier applicatif :

assets/
index.ts ← point d'entrée (généré par Dobby)
js/
app.ts ← code applicatif (généré par Dobby)
interfaces/
elementum.d.ts ← types pour window.ELEMENTUM
global.d.ts ← déclaration globale Window

L'objet global window.ELEMENTUM

Elementum injecte automatiquement un objet ELEMENTUM dans window via wp_localize_script. Il contient les URLs et chemins du plugin, du thème actif et du thème enfant :

assets/js/interfaces/elementum.d.ts
export interface ElementumObject {
    main: { url: string; slug: string };
    theme: { url: string; slug: string };
    child: { url: string; slug: string };
    [key: string]: any;
}
assets/js/interfaces/global.d.ts
import { ElementumObject } from "./elementum";

declare global {
    interface Window {
        ELEMENTUM: ElementumObject;
    }
}
export {};

Ces fichiers sont déjà générés par Dobby — vous pouvez utiliser window.ELEMENTUM directement dans votre TypeScript avec l'autocomplétion.

Écrire du TypeScript dans app.ts

Les scripts sont chargés comme des ES modules (type="module", defer). La bonne pratique est d'organiser votre code en classes dédiées : Menu.ts, Carousel.ts, etc. — chaque classe gère un composant interactif, et app.ts se contente de les instancier.

Créez assets/js/lib/RandomImage.ts :

assets/js/lib/RandomImage.ts
export class RandomImage {
    private img: HTMLImageElement;
    private apiUrl = "https://picsum.photos/800/400";

    constructor(selector: string) {
        const el = document.querySelector<HTMLImageElement>(selector);
        if (!el) return;
        this.img = el;
        this.img.style.cursor = "pointer";
        this.img.addEventListener("click", () => this.refresh());
    }

    private async refresh(): Promise<void> {
        // Picsum renvoie une image aléatoire à chaque requête grâce au timestamp
        const seed = Date.now();
        this.img.src = `${this.apiUrl}?random=${seed}`;
    }
}

Importez-la dans app.ts :

assets/js/app.ts
import { RandomImage } from "./lib/RandomImage";

new RandomImage(".hero-image");

Puis ajoutez l'image cible dans views/front-page.html.twig :

views/front-page.html.twig — ajout dans la section hero
<img src="https://picsum.photos/800/400"
     alt="Image aléatoire"
     class="hero-image w-full rounded-xl mt-8 object-cover max-h-64">

Au clic sur l'image, RandomImage charge une nouvelle photo depuis l'API Picsum — aucun rechargement de page.

Lancer le watcher

npm run dev

Vite surveille les changements dans assets/ et recompile assets/dist/bundle.js à la volée.

Pour aller plus loin

Assets — JS, SCSS & React — Alpine.js, React, modules.


Conclusion

Vous venez de construire un blog complet avec portfolio en pratiquant les concepts fondamentaux d'Elementum. Voici ce que vous avez appris et où chaque concept a été mis en œuvre :

Concept pratiquéFichier
#[Template] sur 6 types différentsThemeController.php
#[Action] pour hooker WordPressThemeController.phpwp_head
CPT + taxonomie + colonnes adminapp/PostTypes/Project.php
Composants Twig réutilisablesviews/components/card/
{% wp_query %} avec et sans argumenthome.html.twig, archives CPT
Customizer YAML + variables CSScustomize/theme.yaml
SCSS organisé + alias @elementumassets/scss/

Récapitulatif : généré par Dobby vs créé manuellement

FichierOrigine
app/Controller/ThemeController.phpDobby — à compléter
views/base.html.twigDobby — à compléter
views/front-page.html.twigDobby — à compléter
app/PostTypes/Project.phpDobby (make:post) — à compléter
views/home.html.twigÀ créer
views/single.html.twigÀ créer
views/archive-project.html.twigÀ créer
views/single-project.html.twigÀ créer
views/components/card/article.html.twigÀ créer
views/components/card/project.html.twigÀ créer
customize/theme.yamlÀ créer
assets/scss/ (sous-dossiers)À créer

Étapes suivantes

  • Thème enfant — surchargez des composants sans modifier le thème parent
  • Hooks Twig — rendez vos composants extensibles avec hook() et #[Hook] : Hook
  • API REST — ajoutez un endpoint AJAX avec #[Api] : API
  • Blocs Gutenberg — créez des blocs custom avec rendu Twig : Blocs