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+ taxonomietechnology) - Couleurs personnalisables via le Customizer
Prérequis : Elementum installé et activé, WordPress fonctionnel, Node.js disponible.
- Clonez le plugin dans
wp-content/plugins/:git clone https://github.com/devopress/elementum wp-content/plugins/elementum - Activez-le dans Extensions de l'administration WordPress, ou via WP-CLI :
wp plugin activate elementum - Une fois activé, Elementum expose une interface d'administration à l'adresse
/elementum/admin— vous pouvez l'utiliser à la place de/wp-adminou/wp-login.phppour 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
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/.
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 :
# 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.
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 :
<!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')etfn('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.urlsont 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 variablemenuest 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.
Variables globales Twig — site, 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 :
<?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 templatearchivesé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 :
{% 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 :
<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 :
{% 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.
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 :
{% 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.
post est une instance de la façace Post.
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 :
<?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 :
#[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 :
{% 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 :
<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 :
{% 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 %}
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 :
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 :
$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 :
@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
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.
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
@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.
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 :
export interface ElementumObject { main: { url: string; slug: string }; theme: { url: string; slug: string }; child: { url: string; slug: string }; [key: string]: any; }
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 :
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 :
import { RandomImage } from "./lib/RandomImage"; new RandomImage(".hero-image");
Puis ajoutez l'image cible dans views/front-page.html.twig :
<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.
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érents | ThemeController.php |
#[Action] pour hooker WordPress | ThemeController.php → wp_head |
| CPT + taxonomie + colonnes admin | app/PostTypes/Project.php |
| Composants Twig réutilisables | views/components/card/ |
{% wp_query %} avec et sans argument | home.html.twig, archives CPT |
| Customizer YAML + variables CSS | customize/theme.yaml |
SCSS organisé + alias @elementum | assets/scss/ |
Récapitulatif : généré par Dobby vs créé manuellement
| Fichier | Origine |
|---|---|
app/Controller/ThemeController.php | Dobby — à compléter |
views/base.html.twig | Dobby — à compléter |
views/front-page.html.twig | Dobby — à compléter |
app/PostTypes/Project.php | Dobby (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 |