<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kahin Warsame</title>
    <description>The latest articles on DEV Community by Kahin Warsame (@kwadigital).</description>
    <link>https://dev.to/kwadigital</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4098301%2Fd88bc3b5-e29d-4bc6-97b9-0a68cfc71eb3.png</url>
      <title>DEV Community: Kahin Warsame</title>
      <link>https://dev.to/kwadigital</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kwadigital"/>
    <language>en</language>
    <item>
      <title>How to Build a Custom-Coded WordPress Site (Custom Theme + Import)</title>
      <dc:creator>Kahin Warsame</dc:creator>
      <pubDate>Fri, 28 Aug 2026 04:27:46 +0000</pubDate>
      <link>https://dev.to/kwadigital/how-to-build-a-custom-coded-wordpress-site-custom-theme-import-2laj</link>
      <guid>https://dev.to/kwadigital/how-to-build-a-custom-coded-wordpress-site-custom-theme-import-2laj</guid>
      <description>&lt;p&gt;Building a &lt;strong&gt;custom-coded WordPress site&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;This is exactly how we build at &lt;a href="https://www.kwadigital.com/" rel="noopener noreferrer"&gt;KWA Digital&lt;/a&gt;, and it is how we built our own site. 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom theme vs. page builder: which should you use?
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Page builder (Elementor, Divi)&lt;/th&gt;
&lt;th&gt;Custom-coded theme&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Speed to start&lt;/td&gt;
&lt;td&gt;Fast, no code needed&lt;/td&gt;
&lt;td&gt;Slower, requires coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page weight &amp;amp; speed&lt;/td&gt;
&lt;td&gt;Heavier, more scripts&lt;/td&gt;
&lt;td&gt;Light, only what you write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design control&lt;/td&gt;
&lt;td&gt;Limited to the builder's system&lt;/td&gt;
&lt;td&gt;Pixel-perfect, total control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;Locked to the builder&lt;/td&gt;
&lt;td&gt;A folder you upload anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term cost&lt;/td&gt;
&lt;td&gt;Ongoing plugin dependency&lt;/td&gt;
&lt;td&gt;You own the code outright&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Quick internal pages&lt;/td&gt;
&lt;td&gt;Distinctive, fast, professional sites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you want a unique, fast site you fully own, a custom theme is worth the extra work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Comfort with HTML, CSS, and a little PHP (less than you think, see the FAQ).&lt;/li&gt;
&lt;li&gt;A local WordPress environment for testing. Local by Flywheel is free and takes minutes to set up.&lt;/li&gt;
&lt;li&gt;A code editor.&lt;/li&gt;
&lt;li&gt;A basic understanding of the WordPress dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Build the design as plain HTML, CSS, and JS first
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One &lt;code&gt;main.css&lt;/code&gt; for all styles, using CSS custom properties (tokens) for colors, spacing, and fonts.&lt;/li&gt;
&lt;li&gt;One &lt;code&gt;main.js&lt;/code&gt; for interactions (menus, animations), written as progressive enhancement so the site still works with JavaScript off.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;assets/img/&lt;/code&gt; folder for images.&lt;/li&gt;
&lt;li&gt;One &lt;code&gt;.html&lt;/code&gt; file per page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Convert the static site into a WordPress theme
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  The theme folder structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  style.css: the theme header
&lt;/h3&gt;

&lt;p&gt;WordPress identifies a theme by this comment block at the top of &lt;code&gt;style.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/*
Theme Name: My Theme
Author: Your Name
Description: A custom hand-coded theme.
Version: 1.0.0
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your actual design CSS can live here, or in &lt;code&gt;assets/css/main.css&lt;/code&gt; and get loaded by the next file.&lt;/p&gt;

&lt;h3&gt;
  
  
  functions.php: load assets and enable features
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mytheme_setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;add_theme_support&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'title-tag'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;add_theme_support&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'post-thumbnails'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;register_nav_menus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Primary Navigation'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'after_setup_theme'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mytheme_setup'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mytheme_assets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'mytheme-main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;get_theme_file_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'assets/css/main.css'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;wp_enqueue_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'mytheme-main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;get_theme_file_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'assets/js/main.js'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mytheme_assets'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key rule:&lt;/strong&gt; never hardcode asset paths. Use &lt;code&gt;get_theme_file_uri()&lt;/code&gt; for images, CSS, and JS, and &lt;code&gt;home_url()&lt;/code&gt; for internal links, so the theme works on any domain or subfolder.&lt;/p&gt;

&lt;h3&gt;
  
  
  header.php and footer.php (defined once, shared everywhere)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- header.php --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;language_attributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;bloginfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'charset'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;wp_head&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;body_class&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"site-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"brand"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;home_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Site&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;wp_nav_menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'theme_location'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- footer.php --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"site-footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;wp_footer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Page templates
&lt;/h3&gt;

&lt;p&gt;Take each static page's &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; content, wrap it with &lt;code&gt;get_header()&lt;/code&gt; and &lt;code&gt;get_footer()&lt;/code&gt;, and rewrite the image and link paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;get_header&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- your page's HTML here, with:
       src="&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;get_theme_file_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'assets/img/photo.jpg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;"
       href="&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;home_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/about/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;" --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nf"&gt;get_footer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;front-page.php&lt;/code&gt; is used automatically for the homepage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page-{slug}.php&lt;/code&gt; is used automatically for a page whose slug matches (for example, &lt;code&gt;page-about.php&lt;/code&gt; renders the page with slug &lt;code&gt;about&lt;/code&gt;). This is how you attach a specific design to a specific page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.php&lt;/code&gt; and &lt;code&gt;index.php&lt;/code&gt; are the fallbacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Make the content editable (no page builder needed)
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog-style content&lt;/strong&gt; (news, press releases): use native Posts, and loop them in a page template with &lt;code&gt;WP_Query&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeating structured items&lt;/strong&gt; (team members, testimonials): register a Custom Post Type with a meta box for extra fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editable page copy&lt;/strong&gt; (headlines, taglines): register fields in the Customizer and read them with &lt;code&gt;get_theme_mod()&lt;/code&gt;. Set the default equal to your shipped copy, so untouched fields always look right.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example Customizer field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'customize_register'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$wp_customize&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$wp_customize&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'mytheme_hero'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Homepage Hero'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$wp_customize&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'hero_tagline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'default'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Your default tagline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'sanitize_callback'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sanitize_text_field'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$wp_customize&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'hero_tagline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'label'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Hero tagline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'section'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'mytheme_hero'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;esc_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;get_theme_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'hero_tagline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Your default tagline'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Package the theme correctly (the number-one gotcha)
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;The gotcha that will cost you an hour:&lt;/strong&gt; some tools, notably Windows PowerShell's &lt;code&gt;Compress-Archive&lt;/code&gt;, 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 &lt;code&gt;zip&lt;/code&gt;, most GUI archivers, or a script that forces &lt;code&gt;/&lt;/code&gt; in the entry names). Always verify the zip's internal paths look like &lt;code&gt;mytheme/style.css&lt;/code&gt;. We learned this one the hard way shipping our own site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Install the theme
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Option A, the WordPress uploader (for small themes):&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B, File Manager or SFTP (if the uploader fails or the theme is large):&lt;/strong&gt; upload the unzipped &lt;code&gt;mytheme&lt;/code&gt; folder directly into &lt;code&gt;wp-content/themes/&lt;/code&gt;, 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Set up pages, homepage, and menu
&lt;/h2&gt;

&lt;p&gt;The theme provides the design; WordPress content fills it in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permalinks:&lt;/strong&gt; Settings → Permalinks → Post name, so &lt;code&gt;/about/&lt;/code&gt; style URLs work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pages:&lt;/strong&gt; create pages with the exact slugs your &lt;code&gt;page-{slug}.php&lt;/code&gt; files expect. Leave the bodies empty if the design lives in the template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Homepage:&lt;/strong&gt; Settings → Reading → "A static page" → pick your Home page (rendered by &lt;code&gt;front-page.php&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Menu:&lt;/strong&gt; Appearance → Menus → add the pages → assign to the Primary Navigation location.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Step 7: Deploy to production safely
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build and test on a local or staging site first.&lt;/li&gt;
&lt;li&gt;To move the finished site to production, a migration plugin (such as All-in-One WP Migration) works, but the free version caps browser uploads at 64MB. For larger sites, place the export file on the server (File Manager or SFTP) and use the plugin's server-side restore, which ignores the upload limit.&lt;/li&gt;
&lt;li&gt;If you only have WordPress admin (no hosting or File Manager access), the reliable alternative is to install the theme and recreate the content directly on production. This is fast when the content is small.&lt;/li&gt;
&lt;li&gt;Always back up the live site before overwriting it, and re-save Settings → Permalinks after any migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common pitfalls we learned the hard way
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zip backslash paths&lt;/strong&gt; cause "missing style.css." Zip with forward slashes and verify the internal paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional function definitions&lt;/strong&gt; (&lt;code&gt;if ( ! function_exists() ) { ... }&lt;/code&gt;) placed below where they are called in the same file: PHP does not hoist them, so you get a critical error. Keep shared helpers in &lt;code&gt;functions.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;backdrop-filter&lt;/code&gt; on a sticky header&lt;/strong&gt; becomes the containing block for a &lt;code&gt;position: fixed&lt;/code&gt; mobile menu on iOS Safari, trapping the overlay behind it. Drop the blur at mobile widths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardcoded URLs&lt;/strong&gt; break on a different domain or subfolder. Always use &lt;code&gt;home_url()&lt;/code&gt; and &lt;code&gt;get_theme_file_uri()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 64MB upload limit&lt;/strong&gt; blocks large imports. Use server-side restore, or rebuild the content on production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know PHP to build a custom WordPress theme?
&lt;/h3&gt;

&lt;p&gt;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: &lt;code&gt;get_header()&lt;/code&gt;, &lt;code&gt;get_footer()&lt;/code&gt;, &lt;code&gt;wp_head()&lt;/code&gt;, 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a custom-coded theme better than Elementor or Divi?
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does WordPress say "the theme is missing the style.css stylesheet"?
&lt;/h3&gt;

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

&lt;h2&gt;
  
  
  Conclusion: when a custom theme is worth it
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.kwadigital.com/custom-coded-wordpress-site" rel="noopener noreferrer"&gt;KWA Digital blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
