Skip to content

Project Structure

A high-level map of what lives where and why.

  • Directorylumbermill/
    • Directoryacf-php/ ACF field group definitions (PHP)
      • …
    • Directoryapp/ Theme PHP application code
      • …
    • Directoryassets/src/ Frontend source files (JS, SCSS, fonts, images)
      • …
    • Directoryconfig/ Developer-facing configuration
      • …
    • Directorytemplates/ WordPress page templates (selectable in admin)
      • …
    • Directorytranslations/ JSON translation files (one per locale)
      • …
    • Directoryviews/ Twig templates
      • …
    • functions.php Theme bootstrap
    • front-page.php Front page WordPress template
    • page.php Default page WordPress template
    • single.php Single post WordPress template

All ACF field groups are defined in PHP here. Organise by context — each subfolder maps to a part of WordPress.

  • Directoryacf-php/
    • Directoryglobal/ Fields attached to all post types (e.g. featured image)
      • …
    • Directorypages/ Fields for specific WordPress pages (e.g. front-page)
      • …
    • Directorypost-types/ Fields for custom post types
      • …
    • Directorytemplates/ Fields for page templates
      • …
    • Directoryoption-pages/ Fields for ACF options pages
      • …
    • Directoryflex/ Flexible content layout definitions
      • …
    • Directoryhelpers/ Shared utilities (createFieldGroup, loadFieldGroup, loadFlexibleLayout, etc.)
      • …
    • index.php Entry point — autoloads every nested index.php

See the ACF guide for how to create field groups.


PHP application code. Everything here is autoloaded under the App\ namespace.

  • Directoryapp/
    • DirectoryCache/ Cache driver implementations
      • …
    • DirectoryClassmap/ Timber class extensions (Post, Term, Menu, MenuItem)
      • …
    • DirectoryConstant/ String constants — PostTypeConstant, TaxonomyConstant, etc.
      • …
    • DirectoryEnum/ PHP enums
      • …
    • DirectoryHook/ WordPress hooks and filters
      • …
    • DirectoryLib/ Framework internals — do not modify
      • …
    • DirectoryUtil/ Reusable utility classes
      • …

Where does new code go?

What you’re buildingFolder
WordPress hook / filterapp/Hook/
Extending a Timber post/term/menuapp/Classmap/
A reusable helperapp/Util/
A post type or taxonomy slugapp/Constant/
An AJAX actionapp/Ajax/Action/ (see Ajax)
A REST endpointapp/RestAPI/ (see REST API)

Frontend source files processed by Vite.

  • Directoryassets/src/
    • Directoryjs/
      • Directorylib/ Shared JS utilities and helpers
        • …
      • Directorymodules/ Feature-specific JS modules
        • …
      • Directoryvendor/ Third-party scripts not managed by npm
        • …
      • site.js Main entrypoint
    • Directoryscss/
      • Directoryabstracts/ Variables, mixins, functions
        • …
      • Directorybase/ Reset, typography, global styles
        • …
      • Directorylayout/ Grid, containers, structural styles
        • …
      • Directorypages/ Page-specific styles
        • …
      • Directorypartials/ Component styles
        • …
      • Directoryvendor/ Third-party CSS overrides
        • …

Plain PHP arrays that control theme behaviour. Every file has a corresponding provider that acts on it at boot time. See the Config section for a full reference of each file.


WordPress page templates — selectable from the WordPress admin when editing a page. Each file here passes data to a Twig template in views/templates/.

See Page Templates for the full workflow.


All Twig templates. Never called directly — always rendered via Timber::render() in a PHP template.

  • Directoryviews/
    • Directorylayouts/ Base HTML layouts (base.twig)
      • …
    • Directorypartials/ Reusable includes (header, footer, head, buttons, images)
      • …
    • Directorytemplates/ Page-level templates, one per PHP template
      • …

The theme bootstrap. Loads config files and boots providers. The only things that belong here are:

  • Custom providers: $config->addProvider(new MyProvider);
  • Custom provider overrides: $config->replaceProvider(ThemeProvider::class, new MyThemeProvider);

Everything else should live in a provider or a hook class.