
Turning a static HTML site into a working WordPress theme can feel like magic — but it’s a repeatable, straightforward process when you break it into steps. Below is a practical, developer-focused guide you can follow start-to-finish, with code snippets, tips, and common pitfalls.
1. Prerequisites (what you need)
- Local development environment: LocalWP, XAMPP, MAMP, Docker, or similar.
- WordPress installed locally.
- Basic PHP, HTML, CSS, JS knowledge.
- The static HTML site (all files:
index.html
,about.html
,css/
,js/
,images/
, etc.). - Code editor (VS Code, PhpStorm).
2. Plan your theme structure
Decide mapping from HTML pages → WP templates:
index.html
→index.php
about.html
→page-about.php
or use a WP Page with custom templateblog.html
→home.php
orarchive.php
- header/footer parts →
header.php
,footer.php
- Reusable sections → template parts (e.g.,
template-parts/hero.php
)
Theme folder structure (inside wp-content/themes/your-theme/
):
your-theme/
├─ style.css
├─ functions.php
├─ index.php
├─ header.php
├─ footer.php
├─ sidebar.php (optional)
├─ single.php
├─ page.php
├─ archive.php
├─ 404.php
├─ template-parts/
│ ├─ hero.php
│ └─ content-card.php
├─ assets/
│ ├─ css/
│ ├─ js/
│ └─ images/
3. Create the bare theme files
- Create theme folder:
wp-content/themes/my-html-theme
. - Minimal
style.css
(theme header):
/*
Theme Name: My HTML Theme
Theme URI: https://example.com
Author: Your Name
Version: 1.0
Text Domain: my-html-theme
*/
- Minimal
index.php
:
<?php get_header(); ?>
<main id="main">
<?php
if ( have_posts() ) {
while ( have_posts() ) { the_post();
the_content();
}
} else {
echo '<p>No posts found.</p>';
}
?>
</main>
<?php get_footer(); ?>
- Add
header.php
andfooter.php
(see step 4).
4. Split your HTML into header/footer and template parts
Take index.html
and move:
<head>
and top nav →header.php
- Footer markup →
footer.php
- In between content →
index.php
or template parts.
Example header.php
:
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<header class="site-header">
<div class="container">
<a class="logo" href="<?php echo esc_url(home_url('/')); ?>">
<img src="<?php echo get_theme_file_uri('assets/images/logo.png'); ?>" alt="<?php bloginfo('name'); ?>">
</a>
<?php
wp_nav_menu([
'theme_location' => 'primary',
'container' => 'nav',
'container_class' => 'site-nav'
]);
?>
</div>
</header>
Example footer.php
:
<footer class="site-footer">
<div class="container">
<p>© <?php echo date('Y'); ?> <?php bloginfo('name'); ?></p>
</div>
</footer>
<?php wp_footer(); ?>
</body>
</html>
5. Enqueue styles and scripts properly (functions.php
)
Never hard-code <link>
or <script>
tags — use wp_enqueue_*
.
Example functions.php
:
<?php
function mytheme_setup() {
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', ['search-form','comment-form','gallery']);
register_nav_menus([
'primary' => __('Primary Menu', 'my-html-theme'),
]);
}
add_action('after_setup_theme', 'mytheme_setup');
function mytheme_scripts() {
// Main stylesheet (style.css must be enqueued)
wp_enqueue_style('mytheme-style', get_stylesheet_uri(), [], '1.0');
// Additional CSS (from assets)
wp_enqueue_style('mytheme-main', get_theme_file_uri('/assets/css/main.css'), ['mytheme-style'], '1.0');
// JS: jQuery is available via WP; add your script
wp_enqueue_script('mytheme-script', get_theme_file_uri('/assets/js/main.js'), ['jquery'], '1.0', true);
// Localize data if needed
wp_localize_script('mytheme-script', 'MyTheme', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mytheme_nonce'),
]);
}
add_action('wp_enqueue_scripts', 'mytheme_scripts');
Notes:
get_stylesheet_uri()
points tostyle.css
.- Put static assets in
assets/
and reference usingget_theme_file_uri()
.
6. Convert static content to dynamic WordPress content
- Replace hard-coded page content with
the_content()
for posts/pages. - For repeating items (cards, blog lists), use WP Loop.
Example loop for blog listing (home.php
/ index.php
):
<?php get_header(); ?>
<section class="blog-list container">
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class('post-card'); ?>>
<a href="<?php the_permalink(); ?>">
<?php if ( has_post_thumbnail() ) the_post_thumbnail('medium'); ?>
<h2><?php the_title(); ?></h2>
</a>
<div class="excerpt"><?php the_excerpt(); ?></div>
</article>
<?php endwhile; the_posts_pagination(); else: ?>
<p>No posts yet.</p>
<?php endif; ?>
</section>
<?php get_footer(); ?>
7. Templates for pages, posts, archives
single.php
— single blog post layout.page.php
— WP page layout.archive.php
/category.php
/tag.php
— listing pages.404.php
— not found page.
If a specific HTML page needs unique layout, create page template:
<?php
/* Template Name: About Custom */
get_header();
?>
<section class="about container">
<?php while ( have_posts() ) : the_post(); the_content(); endwhile; ?>
</section>
<?php get_footer(); ?>
Assign this template when editing the page in WP admin.
8. Register widget areas and theme supports
Add sidebars, footer widgets:
function mytheme_widgets_init() {
register_sidebar([
'name' => 'Primary Sidebar',
'id' => 'sidebar-1',
'before_widget' => '<div class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
]);
}
add_action('widgets_init', 'mytheme_widgets_init');
Add add_theme_support('custom-logo')
if needed, and support for custom-header
or customize-selective-refresh-widgets
.
9. Menus and navigation
Register menu locations in functions.php
(see earlier register_nav_menus
).
In admin: Appearance → Menus → create menu and assign to "Primary".
Style the menu markup to match your original header.
10. Images and media
- Move
images/
intoassets/images/
. - For user-editable content, use WordPress Media Library (featured images).
- Use
the_post_thumbnail()
with registered image sizes:
add_image_size('card-thumb', 400, 250, true);
11. Add dynamic site metadata & SEO-friendly items
Replace static <title>
and meta tags with WP equivalents:
- Use
add_theme_support('title-tag')
and let WP generate titles. - For structured meta, install an SEO plugin (Yoast, RankMath) OR output meta conditionally in
header.php
.
Example adding meta description from post excerpt:
<?php if (is_singular() && has_excerpt()) : ?>
<meta name="description" content="<?php echo esc_attr(get_the_excerpt()); ?>">
<?php endif; ?>
12. Convert forms and interactive parts
- Convert contact forms to use a plugin (Contact Form 7, WPForms) or build via
admin-ajax.php
. - For search, use
get_search_form()
and convert static search box markup to WP search template.
13. Convert site-wide components into template parts
Example: template-parts/hero.php
and include with:
<?php get_template_part('template-parts/hero'); ?>
This keeps templates clean and reusable.
14. Custom Post Types (CPT) and Advanced Content
If your HTML site had sections like “Projects”, “Team”, or “Products”:
- Register CPTs via
register_post_type()
infunctions.php
, or use a plugin (Custom Post Type UI). - Example for Team:
function mytheme_cpt_team() {
register_post_type('team', [
'labels' => ['name' => 'Team'],
'public' => true,
'supports' => ['title','editor','thumbnail'],
'has_archive' => true
]);
}
add_action('init','mytheme_cpt_team');
For structured fields, use ACF (Advanced Custom Fields) or WP native meta boxes.
15. Customize theme options / Customizer
Use the WordPress Customizer API or theme.json
for global settings:
function mytheme_customize_register($wp_customize) {
$wp_customize->add_section('mytheme_header', ['title' => 'Header Options']);
$wp_customize->add_setting('mytheme_phone');
$wp_customize->add_control('mytheme_phone', ['label'=>'Phone','section'=>'mytheme_header']);
}
add_action('customize_register','mytheme_customize_register');
Alternatively, build a small options page with add_theme_page()
.
16. Accessibility, responsive, and performance
- Ensure semantic HTML, ARIA where needed, and proper heading order.
- Test responsive breakpoints (mobile-first recommended).
- Optimize images, minify CSS/JS or use build tools (webpack, gulp), and enable caching via plugins in production.
17. Localization & translation
Make strings translatable using __()
/ _e()
:
_e('Read more', 'my-html-theme');
Load textdomain:
function mytheme_textdomain() {
load_theme_textdomain('my-html-theme', get_template_directory() . '/languages');
}
add_action('after_setup_theme','mytheme_textdomain');
18. Security & hardening (basic)
- Escape output:
esc_html()
,esc_attr()
,esc_url()
. - Nonce-protect AJAX forms:
check_ajax_referer()
. - Keep WP/core/plugins/themes updated.
- Disable file editing in
wp-config.php
:define('DISALLOW_FILE_EDIT', true);
19. Testing & QA
- Test pages, posts, menus, widgets, forms.
- Test logged-in vs logged-out behavior.
- Test different browsers and devices.
- Use WP Debug: in
wp-config.php
setWP_DEBUG
totrue
on dev. - Validate markup (HTML validator) and run Lighthouse for performance/accessibility.
20. Deployment (from local → live)
Options:
- Zip theme folder and upload via Appearance → Themes → Add New → Upload.
- Use version control (Git) + deployment (FTP/SFTP, SSH) to server.
- If using LocalWP, push to live via preferred host tools (some hosts provide one-click push).
Checklist before going live:
- Replace dev URLs (search/replace DB if necessary) — use WP CLI or plugins like Better Search Replace.
- Disable debug on production.
- Configure permalinks (Settings → Permalinks).
- Test forms and analytics.
21. Common pitfalls & troubleshooting
- Broken styles: check
wp_head()
andwp_footer()
hooks (they must exist). - 404 on CSS/JS: ensure
get_theme_file_uri()
paths correct. - PHP errors: enable
WP_DEBUG
locally and check error log. - Menus not working: ensure you call
wp_nav_menu()
and assign menu in admin. - Images not uploading: check file permissions.
22. Example minimal file mapping cheat-sheet
index.html
→index.php
<head>
→header.php
<footer>
→footer.php
- CSS →
assets/css/…
+wp_enqueue_style()
- JS →
assets/js/…
+wp_enqueue_script()
- Reusable block →
template-parts/block.php
- Static blog list → WP Loop in
index.php
orhome.php
23. Quick checklist to finish
- Theme files created and activated
- Header/footer split and
wp_head()
/wp_footer()
present - Styles & scripts enqueued correctly
- Menus registered and assigned
- Widgets registered (if needed)
- Templates for pages, single, archive, 404 added
- CPTs registered where required
- Forms converted and working
- Images/featured images hooked up
- Translation-ready strings
- Security checks and escaping applied
- Tested on mobile and desktop
If you want, I can:
- Convert a specific page of your HTML into WordPress template code (paste the HTML and I’ll produce
header.php
,footer.php
, and the template). - Generate a ready-to-drop
functions.php
andstyle.css
tailored to your static site assets. - Walk through converting a specific component (header with mega-menu, hero slider, or contact form) into WordPress.