Les thèmes WordPress peuvent être systématiquement convertis vers EmDash. Le design visuel, la structure du contenu et les fonctionnalités dynamiques se transfèrent tous en utilisant une approche en trois phases.
Approche en trois phases
-
Extraction du design
Extrayez les variables CSS, les polices, les couleurs et les patterns de mise en page du thème WordPress. Analysez le site en production pour capturer les styles calculés et les points de rupture responsive.
-
Conversion des templates
Convertissez les templates PHP en composants Astro. Associez la hiérarchie de templates WordPress aux routes Astro et transformez les balises de template en appels API EmDash.
-
Fonctionnalités dynamiques
Portez les menus de navigation, les zones de widgets, les taxonomies et les paramètres du site vers leurs équivalents EmDash. Créez un fichier seed pour capturer le modèle de contenu complet.
Phase 1 : Extraction du design
Localiser le CSS et les design tokens
| Fichier | Rôle |
|---|---|
style.css | Feuille de style principale avec en-tête du thème |
assets/css/ | Feuilles de style supplémentaires |
theme.json | Thèmes bloc (WP 5.9+) — tokens structurés |
Extraire les design tokens
| Pattern WordPress | Variable EmDash |
|---|---|
| Police du corps | --font-body |
| Police des titres | --font-heading |
| Couleur primaire | --color-primary |
| Arrière-plan | --color-base |
| Couleur du texte | --color-contrast |
| Largeur du contenu | --content-width |
Créer la mise en page de base
Créez src/layouts/Base.astro avec les variables CSS extraites, la structure header/footer, le chargement des polices et les points de rupture responsive :
---
import { getSiteSettings, getMenu } from "emdash";
import "../styles/global.css";
const { title, description } = Astro.props;
const settings = await getSiteSettings();
const primaryMenu = await getMenu("primary");
const pageTitle = title ? `${title} | ${settings.title}` : settings.title;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{pageTitle}</title>
</head>
<body>
<header>
{settings.logo ? (
<img src={settings.logo.url} alt={settings.title} />
) : (
<span>{settings.title}</span>
)}
<nav>
{primaryMenu?.items.map((item) => (
<a href={item.url}>{item.label}</a>
))}
</nav>
</header>
<main><slot /></main>
</body>
</html>
Phase 2 : Conversion des templates
Correspondance de la hiérarchie de templates
| Template WordPress | Route Astro |
|---|---|
index.php | src/pages/index.astro |
single.php | src/pages/posts/[slug].astro |
single-{post_type}.php | src/pages/{type}/[slug].astro |
page.php | src/pages/[...slug].astro |
archive.php | src/pages/posts/index.astro |
category.php | src/pages/categories/[slug].astro |
tag.php | src/pages/tags/[slug].astro |
search.php | src/pages/search.astro |
404.php | src/pages/404.astro |
header.php / footer.php | Partie de src/layouts/Base.astro |
sidebar.php | src/components/Sidebar.astro |
Correspondance des balises de template
| Fonction WordPress | Équivalent EmDash |
|---|---|
have_posts() / the_post() | getEmDashCollection() |
get_post() | getEmDashEntry() |
the_title() | post.data.title |
the_content() | <PortableText value={post.data.content} /> |
the_excerpt() | post.data.excerpt |
the_permalink() | /posts/${post.slug} |
the_post_thumbnail() | post.data.featured_image |
get_the_date() | post.data.publishedAt |
get_the_category() | getEntryTerms(coll, id, "categories") |
get_the_tags() | getEntryTerms(coll, id, "tags") |
Convertir la boucle (The Loop)
WordPress
<?php while (have_posts()) : the_post(); ?>
<article>
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<?php the_excerpt(); ?>
</article>
<?php endwhile; ?> EmDash
---
import { getEmDashCollection } from "emdash";
import Base from "../../layouts/Base.astro";
const { entries: posts } = await getEmDashCollection("posts", {
where: { status: "published" },
orderBy: { publishedAt: "desc" },
});
---
<Base title="Blog">
{posts.map((post) => (
<article>
<h2><a href={`/posts/${post.slug}`}>{post.data.title}</a></h2>
<p>{post.data.excerpt}</p>
</article>
))}
</Base> Convertir les templates d’article unique
WordPress
<?php get_header(); ?>
<article>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<div class="post-meta">
Posted in: <?php the_category(', '); ?>
</div>
</article>
<?php get_footer(); ?> EmDash
---
import { getEmDashCollection, getEntryTerms } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "../../layouts/Base.astro";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts");
return posts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const categories = await getEntryTerms("posts", post.id, "categories");
---
<Base title={post.data.title}>
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
<div class="post-meta">
Publié dans : {categories.map((cat) => (
<a href={`/categories/${cat.slug}`}>{cat.label}</a>
))}
</div>
</article>
</Base> Convertir les parties de template
Les appels WordPress get_template_part() deviennent des imports de composants Astro. Le partial template-parts/content-post.php devient un composant PostCard.astro que vous importez et rendez dans une boucle.
Phase 3 : Fonctionnalités dynamiques
Menus de navigation
Identifiez les menus dans functions.php et créez les menus EmDash correspondants :
---
import { getMenu } from "emdash";
const menu = await getMenu("primary");
---
{menu && (
<nav class="primary-nav">
<ul>
{menu.items.map((item) => (
<li>
<a href={item.url} aria-current={Astro.url.pathname === item.url ? "page" : undefined}>
{item.label}
</a>
{item.children.length > 0 && (
<ul class="submenu">
{item.children.map((child) => (
<li><a href={child.url}>{child.label}</a></li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
)}
Zones de widgets (barres latérales)
Identifiez les zones de widgets dans le thème et affichez-les :
---
import { getWidgetArea, getMenu } from "emdash";
import { PortableText } from "emdash/ui";
import RecentPosts from "./widgets/RecentPosts.astro";
const sidebar = await getWidgetArea("sidebar");
const widgetComponents = { "core:recent-posts": RecentPosts };
---
{sidebar && sidebar.widgets.length > 0 && (
<aside class="sidebar">
{sidebar.widgets.map(async (widget) => (
<div class="widget">
{widget.title && <h3>{widget.title}</h3>}
{widget.type === "content" && <PortableText value={widget.content} />}
{widget.type === "menu" && (
<nav>
{await getMenu(widget.menuName).then((m) =>
m?.items.map((item) => <a href={item.url}>{item.label}</a>)
)}
</nav>
)}
{widget.type === "component" && widgetComponents[widget.componentId] && (
<Fragment>
{(() => {
const Component = widgetComponents[widget.componentId];
return <Component {...widget.componentProps} />;
})()}
</Fragment>
)}
</div>
))}
</aside>
)}
Correspondance des types de widgets
| Widget WordPress | Type de widget EmDash |
|---|---|
| Text/Custom HTML | type: "content" |
| Custom Menu | type: "menu" |
| Recent Posts | component: "core:recent-posts" |
| Categories | component: "core:categories" |
| Tag Cloud | component: "core:tag-cloud" |
| Search | component: "core:search" |
Taxonomies
Interrogez les taxonomies enregistrées dans le thème :
---
import { getTaxonomyTerms, getEntriesByTerm } from "emdash";
import Base from "../../layouts/Base.astro";
export async function getStaticPaths() {
const genres = await getTaxonomyTerms("genre");
return genres.map((genre) => ({
params: { slug: genre.slug },
props: { genre },
}));
}
const { genre } = Astro.props;
const books = await getEntriesByTerm("books", "genre", genre.slug);
---
<Base title={genre.label}>
<h1>{genre.label}</h1>
{books.map((book) => (
<article>
<h2><a href={`/books/${book.slug}`}>{book.data.title}</a></h2>
</article>
))}
</Base>
Correspondance des paramètres du site
| Customizer WordPress | Paramètre EmDash |
|---|---|
| Site Title | title |
| Tagline | tagline |
| Site Icon | favicon |
| Custom Logo | logo |
| Posts per page | postsPerPage |
Shortcodes vers Portable Text
Les shortcodes WordPress deviennent des blocs personnalisés Portable Text :
WordPress
add_shortcode('gallery', function($atts) {
$ids = explode(',', $atts['ids']);
return '<div class="gallery">...</div>';
}); EmDash
---
const { images } = Astro.props;
---
<div class="gallery">
{images.map((img) => (
<img src={img.url} alt={img.alt || ""} loading="lazy" />
))}
</div>Enregistrez avec PortableText :
<PortableText value={content} components={{ gallery: Gallery }} /> Structure du fichier seed
Capturez le modèle de contenu complet dans un fichier seed. Incluez les paramètres, taxonomies, menus et zones de widgets :
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"meta": { "name": "Ported Theme" },
"settings": { "title": "My Site", "tagline": "Welcome", "postsPerPage": 10 },
"taxonomies": [
{
"name": "category",
"label": "Categories",
"hierarchical": true,
"collections": ["posts"]
}
],
"menus": [
{
"name": "primary",
"label": "Primary Navigation",
"items": [
{ "type": "custom", "label": "Home", "url": "/" },
{ "type": "custom", "label": "Blog", "url": "/posts" }
]
}
],
"widgetAreas": [
{
"name": "sidebar",
"label": "Main Sidebar",
"widgets": [
{
"type": "component",
"componentId": "core:recent-posts",
"props": { "count": 5 }
}
]
}
]
}
Voir Format du fichier seed pour la spécification complète.
Checklist de portage
Phase 1 (Design) : Variables CSS extraites, polices chargées, schéma de couleurs correspondant, points de rupture responsive fonctionnels.
Phase 2 (Templates) : Page d’accueil, articles individuels, archives et page 404 s’affichent tous correctement.
Phase 3 (Dynamique) : Paramètres du site configurés, menus fonctionnels, taxonomies interrogeables, zones de widgets rendues, fichier seed complet.
Cas particuliers
Thèmes enfants
Si le thème a un parent (vérifiez style.css pour Template:), analysez d’abord le thème parent, puis appliquez les surcharges du thème enfant.
Thèmes bloc (FSE)
Les thèmes bloc WordPress 5.9+ utilisent theme.json pour les design tokens et templates/*.html pour le balisage des blocs. Convertissez le balisage des blocs en composants Astro et extrayez les tokens de theme.json.
Constructeurs de pages
Le contenu créé avec Elementor, Divi ou similaires est stocké dans les meta des articles, pas dans les fichiers du thème. Ce contenu s’importe via WXR, pas via le portage du thème. Concentrez le portage du thème sur l’enveloppe — le contenu des constructeurs de pages est rendu via Portable Text après l’import.
Prochaines étapes
- Créer des thèmes — Construire des thèmes EmDash distribuables
- Format du fichier seed — Spécification complète du fichier seed
- Migrer depuis WordPress — Importer le contenu WordPress