Porter des Thèmes WordPress

Sur cette page

Les thèmes WordPress peuvent être convertis systématiquement vers EmDash. Le design visuel, la structure de contenu et les fonctionnalités dynamiques sont tous transférés 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 motifs de mise en page du thème WordPress. Analysez le site en direct pour capturer les styles calculés et les points de rupture responsifs.

  2. Conversion des Templates

    Convertissez les templates PHP en composants Astro. Mappez 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 Jetons de Design

|| Fichier | Objectif | || ------------- | ------------------------------------------ | || style.css | Feuille de style principale avec en-tête du thème | || assets/css/ | Feuilles de style supplémentaires | || theme.json | Thèmes de blocs (WP 5.9+) - jetons structurés |

Extraire les Jetons de Design

|| Modèle WordPress | Variable EmDash | || ----------------- | ------------------ | || Famille de police du corps | --font-body | || Police de titre | --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 en-tête/pied de page, le chargement des polices et les points de rupture responsifs :

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

Mappage de la Hiérarchie des 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 |

Mappage 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

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 Individuels

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">
      Posted in: {categories.map((cat) => (
        <a href={`/categories/${cat.slug}`}>{cat.label}</a>
      ))}
    </div>
  </article>
</Base>

Convertir les Parties de Template

Les appels get_template_part() de WordPress deviennent des importations 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 rendez-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>
)}

Mappage des Types de Widget

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

Mappage des Paramètres du Site

|| Personnalisateur WordPress | Paramètre EmDash | || -------------------- | ---------------- | || Site Title | title | || Tagline | tagline | || Site Icon | favicon | || Custom Logo | logo | || Posts per page | postsPerPage |

Shortcodes vers Texte Portable

Les shortcodes WordPress deviennent des blocs personnalisés de Texte Portable :

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 le composant 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, les taxonomies, les menus et les 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 }
				}
			]
		}
	]
}

Consultez Format de Fichier Seed pour la spécification complète.

Liste de Contrôle du Portage

Phase 1 (Design) : Variables CSS extraites, polices chargées, schéma de couleurs correspondant, points de rupture responsifs fonctionnent.

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 de Blocs (FSE)

Les thèmes de blocs WordPress 5.9+ utilisent theme.json pour les jetons de design et templates/*.html pour le balisage des blocs. Convertissez le balisage des blocs en composants Astro et extrayez les jetons de theme.json.

Constructeurs de Pages

Le contenu créé avec Elementor, Divi ou similaire est stocké dans les métadonnées de publication, pas dans les fichiers de thème. Ce contenu s’importe via WXR, pas via le portage de thème. Concentrez le portage de thème sur la coque. Le contenu du constructeur de pages s’affiche via Texte Portable après l’importation.

Prochaines Étapes