Skip to main content

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.htmlindex.php
  • about.htmlpage-about.php or use a WP Page with custom template
  • blog.htmlhome.php or archive.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

  1. Create theme folder: wp-content/themes/my-html-theme.
  2. 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
*/
  1. 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(); ?>
  1. Add header.php and footer.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>&copy; <?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 to style.css.
  • Put static assets in assets/ and reference using get_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/ into assets/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() in functions.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 set WP_DEBUG to true 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() and wp_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.htmlindex.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 or home.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 and style.css tailored to your static site assets.
  • Walk through converting a specific component (header with mega-menu, hero slider, or contact form) into WordPress.

 

Tags