How to Build a WordPress Theme from Scratch
A WordPress theme controls the appearance and layout of a site’s pages. A block theme is a practical starting point for a…
When you build a custom WordPress theme, you decide how site content appears and how editors work with layouts. A theme is the set of files that controls a site’s presentation. Keep the theme focused on how the site looks. Put lasting features, such as custom content types, in a plugin so they remain available when the site changes themes.
Choose a block or classic theme architecture based on the site’s editing needs and compatibility requirements. Build templates and configure editor settings according to WordPress conventions. Follow its security and accessibility guidance, and use its conventions for loading assets. These choices make the theme easier to maintain and help keep design changes from disrupting content or site features.
WordPress supports block themes and classic themes. A block theme uses HTML templates made from blocks and lets editors change areas such as headers and footers in the Site Editor. A classic theme uses PHP templates and remains supported. WordPress also lets classic themes use theme.json, the configuration file for shared editor and front-end settings. The WordPress theme documentation describes both approaches.
Use a block theme when editors need to manage site-wide layouts through the Site Editor. Use a classic theme when PHP templates and an established classic codebase better fit the project. Before development, document the minimum WordPress version the theme must support and the editing tasks the site must enable.
WordPress selects templates through a template hierarchy: it looks for the most specific template for a page or content type, then falls back to a more general one. In a classic theme, index.php is the fallback template; in a block theme, templates/index.html is the minimum required template. Follow the template hierarchy so pages behave predictably when a more specific template is absent.
Use theme.json to define design settings such as colors, typography, spacing, and which block controls editors can use. WordPress applies these settings in the built-in editors and on the front end, helping keep the editing experience aligned with the published design. A classic theme can use theme.json as well as a block theme.
Set a small, deliberate palette and type scale, then use those settings consistently in templates and blocks. This gives editors useful choices while reducing one-off formatting that can make pages inconsistent. Before relying on a setting, check that the theme.json schema supports the project’s minimum WordPress version. Official pages do not consistently list which versions support each setting.
Put features that must survive a theme change in a plugin. For example, register a custom post type in a plugin if the site needs that content type after a redesign. The theme can still provide the templates that display it. WordPress recommends keeping themes focused on presentation and placing behavior and durable features in plugins, as described in its guidance on what a theme does and post types.
If you plan to submit a theme to the WordPress.org directory, check its review rules separately. Directory restrictions, including rules about custom post types, apply to that distribution route; they are not automatically requirements for every private client theme. The theme release guidance explains those directory-specific expectations.
Escape dynamic values when the template prints them. Escaping converts a value so the browser treats it as the intended kind of content in that location. Choose the function for the output context: use esc_html() for text inside HTML and esc_url() for a URL in an attribute. WordPress recommends escaping as late as possible, at the point of output, because the correct treatment depends on where the value appears. See the escaping guidance.
When theme code accepts input, validate that it matches the expected format and sanitize it before use or storage. For actions such as form submissions, use a nonce where appropriate to verify that the request came from an intended user action. A value from the database or an external source still needs the right handling before the theme outputs it. WordPress’s security guidance covers these practices.
Design for keyboard use and provide clear focus indicators. Use headings that describe their content and write descriptive link text. Give images useful alternative text. These choices affect how people navigate and understand the site, so include them in templates and design decisions rather than waiting until the end of development. WordPress’s theme accessibility guidance provides implementation advice.
Use WCAG 2.2 Level AA as a measurable reference for text contrast: the guideline sets a contrast ratio of at least 4.5:1 for most text and 3:1 for large text, with defined exceptions. It also requires text to resize to 200% without loss of content or functionality within the criterion’s scope. Check pages at larger text sizes and test menus, forms, and other controls with a keyboard. The WCAG 2.2 standard defines the criteria. WordPress’s “Accessibility Ready” designation does not by itself mean a theme conforms to WCAG AA.
Use WordPress’s enqueue functions to register front-end styles and scripts instead of adding asset links directly to templates. The wp_enqueue_scripts hook loads these front-end files. Avoid shipping scripts and styles that the theme does not use, and optimize bundled images and other assets. WordPress identifies themes as one factor in site performance. Hosting, plugins, caching, and media also affect speed, so a theme alone cannot guarantee a fast site. See the WordPress performance guidance.
Measure the site after deployment when setting performance goals. The Core Web Vitals “good” thresholds are LCP at or below 2,500 milliseconds, INP at or below 200 milliseconds, and CLS at or below 0.1, measured at the 75th percentile of page views. LCP measures how quickly the main content appears, INP measures responsiveness to interactions, and CLS measures unexpected layout movement. These are field thresholds for the site experience, not pass criteria for a theme in isolation. The Core Web Vitals thresholds explain how to interpret them.
A polished demo page does not reveal every template problem. Test long titles and missing images. Try images of different sizes, comments, and pagination. Include mixed HTML. These cases reveal clipping, awkward spacing, and broken layouts that ordinary sample content may hide. WordPress provides Theme Unit Test Data and recommends checking responsiveness, block behavior, and debugging issues.
Also test the editing experience. For a block theme, edit templates and global styles in the Site Editor and check that editors can make expected changes without breaking the design. For either architecture, navigate with a keyboard and review pages at narrow widths. Automated checks can help find problems, but they do not replace manual interaction testing.
Wrap interface text that belongs to the theme in WordPress translation functions. Use a text domain, the identifier WordPress uses for a theme’s translations, that matches the theme’s slug. This lets translators provide localized wording without changing theme code. Use translation-and-escape functions when outputting translated text in a context that requires escaping. WordPress handles translations for themes hosted in its Theme Directory; other distribution methods can require a different translation-file setup. See the theme internationalization guidance.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com