Porter des thèmes WordPress

Sur cette page

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

  1. 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.

  2. 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.

  3. 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

FichierRôle
style.cssFeuille de style principale avec en-tête du thème
assets/css/Feuilles de style supplémentaires
theme.jsonThèmes bloc (WP 5.9+) — tokens structurés

Extraire les design tokens

Pattern WordPressVariable 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 WordPressRoute Astro
index.phpsrc/pages/index.astro
single.phpsrc/pages/posts/[slug].astro
single-{post_type}.phpsrc/pages/{type}/[slug].astro
page.phpsrc/pages/[...slug].astro
archive.phpsrc/pages/posts/index.astro
category.phpsrc/pages/categories/[slug].astro
tag.phpsrc/pages/tags/[slug].astro
search.phpsrc/pages/search.astro
404.phpsrc/pages/404.astro
header.php / footer.phpPartie de src/layouts/Base.astro
sidebar.phpsrc/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

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 WordPressType de widget EmDash
Text/Custom HTMLtype: "content"
Custom Menutype: "menu"
Recent Postscomponent: "core:recent-posts"
Categoriescomponent: "core:categories"
Tag Cloudcomponent: "core:tag-cloud"
Searchcomponent: "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 WordPressParamètre EmDash
Site Titletitle
Taglinetagline
Site Iconfavicon
Custom Logologo
Posts per pagepostsPerPage

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