← Blog Web design

How to Build a Custom-Coded WordPress Site (Custom Theme + Import)

Hand-coded PHP in a code editor with WordPress theme files in the sidebar

Building a custom-coded WordPress site means designing your site as hand-written HTML, CSS, and JavaScript, then converting that design into a real WordPress theme, so you get a pixel-perfect, fast, fully portable site without a page builder in sight. The design becomes a folder of files you completely control, and moving it to another WordPress install is a single upload.

This is exactly how we build at KWA Digital, and it is how we built the site you are reading now. Page builders have their place, but for a distinctive, performance-first site, hand-coding the design and wrapping it in a custom theme wins on speed, control, and portability every time. This guide walks the whole path: design the site statically, convert it into a theme, make the content client-editable, package it correctly, and get it live, with the specific gotchas that cost us hours the first time so they do not cost you the same.

Custom theme vs. page builder: which should you use?

Before the how, the why. Page builders (Elementor, Divi) are quick and need no code, but that convenience has a cost. Here is the honest trade-off:

FactorPage builder (Elementor, Divi)Custom-coded theme
Speed to startFast, no code neededSlower, requires coding
Page weight & speedHeavier, more scriptsLight, only what you write
Design controlLimited to the builder's systemPixel-perfect, total control
PortabilityLocked to the builderA folder you upload anywhere
Long-term costOngoing plugin dependencyYou own the code outright
Best forQuick internal pages, non-technical ownersDistinctive, fast, professional sites

If you want a unique, fast site you fully own, a custom theme is worth the extra work. Here is the full pipeline:

Static siteHTML / CSS / JS Theme filesstyle.css, functions.php Package + installzip, upload, activate Live sitefast & editable
The pipeline: design statically, convert to theme files, package and install, then a live, editable WordPress site.

What you need before you start

Step 1: Build the design as plain HTML, CSS, and JS first

Before you touch WordPress, build the site as a static prototype. Keeping design and platform concerns separate lets you nail the look fast, without fighting the CMS at the same time. Keep it lean:

Test the static site in a browser until every page looks right on desktop and mobile. This static folder becomes the blueprint for your theme.

Step 2: Convert the static site into a WordPress theme

A WordPress theme is just a folder inside wp-content/themes/. At minimum it needs style.css and index.php, but a real theme uses several template files.

The theme folder structure

mytheme/
  style.css        (required: theme header + or import your CSS)
  functions.php    (enqueue assets, register menus, theme features)
  header.php       (doctype, head, site header/nav)
  footer.php       (site footer, wp_footer)
  front-page.php   (the homepage)
  page.php         (default page template)
  index.php        (required fallback)
  single.php       (single blog post)
  404.php
  assets/
    css/main.css
    js/main.js
    img/

style.css: the theme header

WordPress identifies a theme by this comment block at the top of style.css:

/*
Theme Name: My Theme
Author: Your Name
Description: A custom hand-coded theme.
Version: 1.0.0
*/

Your actual design CSS can live here, or in assets/css/main.css and get loaded by the next file.

functions.php: load assets and enable features

<?php
function mytheme_setup() {
    add_theme_support( 'title-tag' );
    add_theme_support( 'post-thumbnails' );
    register_nav_menus( array( 'primary' => 'Primary Navigation' ) );
}
add_action( 'after_setup_theme', 'mytheme_setup' );

function mytheme_assets() {
    wp_enqueue_style( 'mytheme-main',
        get_theme_file_uri( 'assets/css/main.css' ), array(), '1.0.0' );
    wp_enqueue_script( 'mytheme-main',
        get_theme_file_uri( 'assets/js/main.js' ), array(), '1.0.0', true );
}
add_action( 'wp_enqueue_scripts', 'mytheme_assets' );

Key rule: never hardcode asset paths. Use get_theme_file_uri() for images, CSS, and JS, and home_url() for internal links, so the theme works on any domain or subfolder.

<!-- header.php -->
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
  <meta charset="<?php bloginfo( 'charset' ); ?>">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<header class="site-header">
  <a class="brand" href="<?php echo esc_url( home_url( '/' ) ); ?>">My Site</a>
  <?php wp_nav_menu( array( 'theme_location' => 'primary' ) ); ?>
</header>
<main id="main">
<!-- footer.php -->
</main>
<footer class="site-footer">...</footer>
<?php wp_footer(); ?>
</body>
</html>

Page templates

Take each static page's <main> content, wrap it with get_header() and get_footer(), and rewrite the image and link paths:

<?php get_header(); ?>
  <!-- your page's HTML here, with:
       src="<?php echo esc_url( get_theme_file_uri('assets/img/photo.jpg') ); ?>"
       href="<?php echo esc_url( home_url('/about/') ); ?>" -->
<?php get_footer(); ?>

Step 3: Make the content editable (no page builder needed)

Hardcoded templates are fast but not client-editable. Add editability where it matters, using built-in WordPress features and no paid plugins:

An example Customizer field:

add_action( 'customize_register', function( $wp_customize ) {
    $wp_customize->add_section( 'mytheme_hero', array( 'title' => 'Homepage Hero' ) );
    $wp_customize->add_setting( 'hero_tagline', array(
        'default' => 'Your default tagline',
        'sanitize_callback' => 'sanitize_text_field',
    ) );
    $wp_customize->add_control( 'hero_tagline', array(
        'label' => 'Hero tagline', 'section' => 'mytheme_hero', 'type' => 'text',
    ) );
} );

And in the template:

<?php echo esc_html( get_theme_mod( 'hero_tagline', 'Your default tagline' ) ); ?>

Step 4: Package the theme correctly (the number-one gotcha)

To install via the WordPress uploader, zip the theme folder so the archive contains mytheme/style.css, not a flat pile of loose files.

The gotcha that will cost you an hour: some tools, notably Windows PowerShell's Compress-Archive, write backslash paths into the zip. PHP reads those as one long filename, so WordPress reports "The theme is missing the style.css stylesheet." Zip with a tool that uses forward slashes (macOS or Linux zip, most GUI archivers, or a script that forces / in the entry names). Always verify the zip's internal paths look like mytheme/style.css. We learned this one the hard way shipping our own site.

Step 5: Install the theme

Option A, the WordPress uploader (for small themes): Appearance → Themes → Add New → Upload Theme → select the zip → Install → Activate. The default upload cap is often 64MB, but a code-only theme is tiny, so this is fine.

Option B, File Manager or SFTP (if the uploader fails or the theme is large): upload the unzipped mytheme folder directly into wp-content/themes/, then activate from Appearance → Themes. This bypasses the uploader entirely, which is how we moved our design onto IONOS-hosted WordPress without fighting upload limits.

Step 6: Set up pages, homepage, and menu

The theme provides the design; WordPress content fills it in.

Slug gotcha: if the site already has pages using those slugs, new pages get -2 appended and the template will not attach. Trash or rename the conflicting old pages, then set the correct slug.

Step 7: Deploy to production safely

Common pitfalls we learned the hard way

Frequently asked questions

Do I need to know PHP to build a custom WordPress theme?

You need a little, but far less than people expect. If you are comfortable with HTML and CSS, the PHP in a theme is mostly a handful of template tags: get_header(), get_footer(), wp_head(), and a few functions to load your assets and register a menu. You can build a solid custom theme knowing only those patterns, and pick up more PHP as you add editable content.

Is a custom-coded theme better than Elementor or Divi?

It depends on the goal. Page builders are faster to start and need no code, but they add page weight, lock your design into their system, and are hard to move between sites. A custom-coded theme is more upfront work but produces a lighter, faster, pixel-perfect site that you fully control and can move to another host with a single upload. For a distinctive, performance-focused site, custom wins; for a quick internal page, a builder is fine.

Why does WordPress say "the theme is missing the style.css stylesheet"?

Almost always because the theme zip contains backslash file paths instead of forward slashes. Some tools, notably Windows PowerShell's Compress-Archive, write paths like mytheme\style.css, and PHP reads that whole string as a single filename, so it never finds style.css. Re-zip the folder with a tool that uses forward slashes and confirm the entries look like mytheme/style.css.

Conclusion: when a custom theme is worth it

Choose a custom-coded theme when you want a distinctive, fast, portable site with full control over your markup and performance, and when the client still needs to edit content, which you enable through the Customizer, Posts, and Custom Post Types. It is more upfront work than a page builder, but the result is lighter, faster, uniquely yours, and trivial to move between hosts.

This is the approach we take on every build at KWA Digital: design custom, hand-code it, and hand over a WordPress site the client can actually run. If you would rather have that done for you, that is exactly what we do, and it starts with understanding what a professional website really costs. You can also read how we think about a site that looks great but is not converting.

Want a custom-built, fast, genuinely yours website without touching a line of PHP? Explore our services or get in touch.

Want the custom build without the code?

We design and hand-code premium websites, then hand them over as WordPress sites you can edit yourself. Tell us what you have in mind and we will show you a demo built on your brand.

Request a free demo