Porting WordPress Themes

In questa pagina

I temi WordPress possono essere convertiti sistematicamente a EmDash. Il design visivo, la struttura dei contenuti e le funzionalità dinamiche si trasferiscono utilizzando un approccio in tre fasi.

Approccio in tre fasi

  1. Estrazione del design

    Estrai variabili CSS, font, colori e pattern di layout dal tema WordPress. Analizza il sito live per catturare gli stili calcolati e i breakpoint responsive.

  2. Conversione dei template

    Converti i template PHP in componenti Astro. Mappa la gerarchia dei template WordPress alle route Astro e trasforma i template tag in chiamate API EmDash.

  3. Funzionalità dinamiche

    Porta i menu di navigazione, le aree widget, le tassonomie e le impostazioni del sito ai loro equivalenti EmDash. Crea un file seed per catturare il modello di contenuto completo.

Fase 1: Estrazione del design

Individuare CSS e design token

FileScopo
style.cssFoglio di stile principale con header del tema
assets/css/Fogli di stile aggiuntivi
theme.jsonTemi a blocchi (WP 5.9+) - token strutturati

Estrarre i design token

Pattern WordPressVariabile EmDash
Body font family--font-body
Heading font--font-heading
Primary color--color-primary
Background--color-base
Text color--color-contrast
Content width--content-width

Creare il layout base

Crea src/layouts/Base.astro con le variabili CSS estratte, la struttura header/footer, il caricamento dei font e i breakpoint 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>

Fase 2: Conversione dei template

Mappatura della gerarchia dei template

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.phpParte di src/layouts/Base.astro
sidebar.phpsrc/components/Sidebar.astro

Mappatura dei template tag

Funzione WordPressEquivalente 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")

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

Conversione dei template singoli

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>

Conversione dei template part

Le chiamate WordPress get_template_part() diventano import di componenti Astro. Il partial template-parts/content-post.php diventa un componente PostCard.astro che importi e renderizzi in un loop.

Fase 3: Funzionalità dinamiche

Identifica i menu in functions.php e crea i corrispondenti menu EmDash:

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

Aree widget (Sidebar)

Identifica le aree widget nel tema e renderizzale:

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

Mappatura dei tipi di widget

Widget WordPressTipo 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"

Tassonomie

Interroga le tassonomie registrate nel tema:

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

Mappatura delle impostazioni del sito

WordPress CustomizerImpostazione EmDash
Site Titletitle
Taglinetagline
Site Iconfavicon
Custom Logologo
Posts per pagepostsPerPage

Shortcode e Portable Text

Gli shortcode WordPress diventano blocchi personalizzati 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>

Registra con PortableText:

<PortableText value={content} components={{ gallery: Gallery }} />

Struttura del file seed

Cattura il modello di contenuto completo in un file seed. Includi impostazioni, tassonomie, menu e aree widget:

{
	"$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 }
				}
			]
		}
	]
}

Vedi Formato file seed per la specifica completa.

Checklist di porting

Fase 1 (Design): Variabili CSS estratte, font caricati, schema colori corrispondente, breakpoint responsive funzionanti.

Fase 2 (Template): Homepage, articoli singoli, archivi e pagina 404 si renderizzano correttamente.

Fase 3 (Dinamica): Impostazioni del sito configurate, menu funzionanti, tassonomie interrogabili, aree widget renderizzate, file seed completo.

Casi limite

Temi figlio

Se il tema ha un genitore (controlla style.css per Template:), analizza prima il tema genitore, poi applica le sovrascritture del tema figlio.

Temi a blocchi (FSE)

I temi a blocchi WordPress 5.9+ usano theme.json per i design token e templates/*.html per il markup dei blocchi. Converti il markup dei blocchi in componenti Astro e estrai i token da theme.json.

Page builder

I contenuti creati con Elementor, Divi o simili sono memorizzati nei meta del post, non nei file del tema. Questi contenuti si importano tramite WXR, non tramite il porting del tema. Concentra il porting del tema sul guscio — i contenuti del page builder vengono renderizzati tramite Portable Text dopo l’importazione.

Prossimi passi