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
-
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.
-
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.
-
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
| File | Scopo |
|---|---|
style.css | Foglio di stile principale con header del tema |
assets/css/ | Fogli di stile aggiuntivi |
theme.json | Temi a blocchi (WP 5.9+) - token strutturati |
Estrarre i design token
| Pattern WordPress | Variabile 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 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 | Parte di src/layouts/Base.astro |
sidebar.php | src/components/Sidebar.astro |
Mappatura dei template tag
| Funzione WordPress | Equivalente 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
Menu di navigazione
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 WordPress | Tipo 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" |
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 Customizer | Impostazione EmDash |
|---|---|
| Site Title | title |
| Tagline | tagline |
| Site Icon | favicon |
| Custom Logo | logo |
| Posts per page | postsPerPage |
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
- Creare temi — Costruire temi EmDash distribuibili
- Formato file seed — Specifica completa dei file seed
- Migrare da WordPress — Importare contenuti WordPress