Starter Theme Generator

A working classic PHP theme as files: the template hierarchy, functions.php with the supports that matter, and an optional Tailwind build.

Enable JavaScript to change the fields; the files below are for the defaults.

Goes in the style.css header, which is how WordPress finds the theme at all.

Leave empty to use the theme name. This is also the default text domain.

Must match the folder name for WordPress.org translations to load. Empty uses the folder name.

Every function in functions.php starts with this. Empty derives it from the folder name.

Theme supports

title-tag is required by the theme directory and by most SEO plugins. html5 and editor-styles are expanded into the calls they actually need.

Create this folder: wp-content/themes/acme-starter

Block themes are the default, and there is a generator here for those. This makes the other kind: the PHP theme with a template hierarchy, which is still what you want when the design is not expressible as block templates, when templates need their own queries, or when a client’s plugins expect the_content() inside a loop in a file you control.

What it writes is deliberately small. An underscores-style starter ships a couple of thousand lines, including a navigation script, a customizer stub and a set of template tags, most of which get deleted on day one. This writes the files the hierarchy actually requires, the theme supports that matter, and nothing you have to read before you can remove it.

How to use

  1. Name the theme. The folder name and text domain follow it unless you set them.
  2. Choose the theme supports. title-tag is required by the theme directory and by most SEO plugins.
  3. Decide about menus, a widget area and Tailwind.
  4. Copy the files into wp-content/themes/your-theme/ and activate.

Example

The default set produces thirteen files:

wp-content/themes/acme-starter/
  style.css        the header WordPress reads to find the theme
  functions.php    setup, assets, menus
  header.php       doctype to the opening main, with wp_head and wp_body_open
  footer.php       the close, with wp_footer
  index.php        the fallback, and the only file WordPress insists on
  single.php       a post, with comments and post navigation
  page.php         a page
  archive.php      categories, tags, authors, dates, taxonomies
  search.php       results, with the search form when there are none
  404.php          a search form and a way back
  comments.php     so core does not fall back to theme-compat
  template-parts/content.php   one entry, called by all of the above

Turning on the widget area adds sidebar.php and the register_sidebar() call; turning on Tailwind adds package.json, tailwind.config.js and src/input.css, and points the stylesheet enqueue at build/style.css.

Pitfalls

wp_head(), wp_body_open() and wp_footer() are not optional. Plugins hook all three. A theme missing one breaks analytics, the admin bar and a good part of the plugin directory, and the failure is silent.

The text domain must match the folder name for translations from WordPress.org to load. It is the single commonest reason a translated theme shows English.

Version your stylesheet by its modification time. The generated functions.php uses filemtime(). A hard-coded version string is the reason a client says they cannot see the change: their browser still has the old file.

Every function needs a prefix. PHP has one global namespace for functions, so setup() in a theme is a fatal error waiting for the plugin that also defines it.

comments.php matters even if you do not want comments. Without it, core falls back to wp-includes/theme-compat/comments.php and emits a deprecation notice that sits in a log for years.

the_content(), not echo $post->post_content. The filters are what make shortcodes, blocks and embeds work; skipping them breaks half the plugins on a site in a way that looks like the plugins’ fault.

A classic theme does not get core block styles automatically. wp-block-styles is in the supports list for that reason: without it, blocks in post content render unstyled on the front end.

Tailwind’s preflight strips the margins post content relies on. The generated input.css puts them back inside .entry-content rather than turning preflight off, which would cost you the rest of the reset.

Check the Tailwind content globs when you add a directory. A class that only appears in a file the globs miss is a class that works locally and vanishes in production.

Compatibility

Every file is also downloadable as a single .zip, built in your browser. The archive stores the files rather than deflating them, which is what WordPress’s own installer reads, and the whole set goes in a folder named after the theme, so Appearance → Themes → Add New → Upload installs it directly. The writer was checked by installing what it produces through WP-CLI on WordPress 7.1.2, activating it, and loading the home page.

Targets WordPress 6.5 and PHP 7.4 and up, both declared in the style.css header so the Themes screen can refuse an older install.

The generated theme was checked against WordPress rather than against itself: the files were written into wp-content/themes, the theme was activated on a WordPress 7.1.2 install, and the home page, a search and a 404 were requested. All three rendered, with entries, pagination and the footer credit, and nothing in the log.

Both languages generate the files here: the browser builds them as you type, and PHP builds the same ones for the first paint, which is why the page works with JavaScript off. The build compares the two outputs across seven cases, including hostile input, so a change to one that is not made to the other fails the build rather than showing up as a page that changes after it loads.

Every PHP file of every test case goes through php -l. Generated code that does not parse is worse than no generator, and the hostile case puts quotes, backslashes, */ and ?> through the theme name, author, prefix and text domain.

The theme does not include a screenshot.png, a readme.txt or a languages folder. The first is a picture, the second has its own generator here, and the third is empty until you have strings to translate.

Frequently asked questions

Should I start a new site with this or with a block theme?
A block theme, unless you have a reason. The reasons are real though: heavy custom queries in templates, a design the block editor fights, or a team that writes PHP and not React.
Is this underscores?
No, and it is much smaller. Underscores gives you a starting point with opinions about navigation and the customizer; this gives you the hierarchy and gets out of the way.
Can I add a block editor to it?
It already works: a classic theme renders blocks in post content. Turning on wp-block-styles, align-wide and editor-styles is what makes the editor and the front end agree.
How do I add a custom template?
Add the file and the hierarchy finds it: single-book.php for a post type, taxonomy-genre.php for a taxonomy, page-about.php for a specific page. The generated content.php is the part you reuse.
What about a child theme?
Use the child theme generator here instead. A child theme of this one is only worth it if someone else maintains the parent.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.