Files
Portfolio-2026/docs/architecture.md
T
Keith SolomonandClaude Opus 4.7 b3cc21a860 docs: Add onboarding documentation for new developers
Add four comprehensive guides to help new developers get started
with the VDI-Starter-v5 WordPress theme:

- docs/getting-started.md: Setup from zero, local WordPress options,
  env config, theme activation warnings, troubleshooting
- docs/architecture.md: Bootstrap flow, 7 architectural layers,
  namespace conventions, WP hooks cleanup, enqueue system, theme.json
- docs/creating-blocks.md: Step-by-step ACF block creation tutorial,
  helper functions, parent-child patterns, Tailwind integration
- docs/reference.md: Hooks/filters tables, design tokens, CSS import
  tree, JS module graph, class reference, CLI commands, deployment

Update README.md: trim deep-dive API docs (moved to reference),
add documentation links section, fix CSS paths (views/styles/ →
styles/), add missing contact-info block, fix deployment filename
typo (wpengine,yml → wpengine.yml), add backToTop.js and
enqEditorAssets(), add namespace conventions table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 17:41:32 -05:00

21 KiB

Architecture

A deep dive into how VDI-Starter-v5 is organized, how it boots, and the conventions it uses.

Table of Contents

Bootstrap Flow

When WordPress loads a theme, it starts with style.css (for theme metadata) and functions.php (for logic). Here's exactly what happens in VDI-Starter-v5:

WordPress loads the theme
  │
  ├─ style.css          → Theme declaration (name, description, version)
  │
  └─ functions.php      → Entry point
       │
       ├─ namespace BasicWP
       │
       ├─ glob(__DIR__ . '/lib/*.php')   → Autoloads every PHP file in lib/
       │    ├─ activation.php            → Theme activation handler (runs once)
       │    ├─ class-acf.php             → ACF JSON sync paths
       │    ├─ class-breadcrumbs.php      → Breadcrumb generation
       │    ├─ class-enqueue.php         → Asset loading (CSS, JS, fonts)
       │    ├─ class-menuitems.php       → Nav menu rendering
       │    ├─ class-resources.php        → Custom post type
       │    ├─ extras.php                → Sidebar, page header, Owner role, etc.
       │    ├─ helpers.php                → Utility functions, globals, ACF options page
       │    ├─ hooks.php                  → WordPress hooks, cleanup, SVG support
       │    ├─ search-features.php        → Enhanced search
       │    └─ show-template.php          → Debug template path display
       │
       └─ regACFBlocks()                → Registers ACF blocks (init hook, priority 5)
            └─ Scans views/blocks/*/block.json (skips 'boilerplate')

The glob autoload means every file in lib/ is loaded on every request. This is intentional — it keeps the architecture flat and predictable. If you add a new file to lib/, it's automatically available without modifying functions.php.

When the init hook fires (priority 1), hooks.php::init() runs its cleanup routine and adds theme supports. Then at priority 5, regACFBlocks() registers all ACF blocks. The Enqueue class constructor hooks into wp_enqueue_scripts, admin_enqueue_scripts, and enqueue_block_editor_assets.

Architectural Layers

The theme is organized into seven distinct layers, each with a clear responsibility:

1. Entry Layer

The two files WordPress needs to recognize the theme:

File Purpose
functions.php Autoloads lib/*.php, registers ACF blocks on init
style.css Theme declaration — name, description, version, author

Why it matters: functions.php is the only file you should never edit directly. All logic lives in lib/.

2. Service Layer

PHP classes and utility functions in lib/ that provide core functionality:

File Class/Function Purpose
class-enqueue.php Enqueue Loads all frontend CSS, JS, and fonts with cache-busting
class-menuitems.php MenuItems Resolves WordPress nav menus into renderable item trees
class-breadcrumbs.php Breadcrumbs Context-aware breadcrumb trails with Schema.org markup
class-acf.php ACF Configures ACF JSON save/load paths for version control
class-resources.php Resources Registers the "Resources" custom post type with URL rewriting
class-resources.php ShowTemplate Adds HTML comment to footer showing active template (debug)
hooks.php init() Aggressive WordPress cleanup, theme supports, SVG uploads
helpers.php Various getFieldValue(), blockWrapperAttributes(), blockCategories(), etc.
extras.php Various createOwnerRole(), hasSidebar(), hasPageHeader(), divWrapper()
search-features.php Various pageSearch(), dedupe(), postSort(), searchResultFilter()
activation.php Various Auto-installs plugins, creates pages, configures settings on theme activation

3. UI Templates and Components

WordPress template hierarchy files, ACF blocks, reusable components, and icons:

Template Hierarchy:

File WordPress Template For
front-page.php The front page
index.php Blog posts listing (fallback for all)
single.php Individual posts
page.php Static pages (with optional sidebar)
search.php Search results
404.php Page not found
header.php Site header (included by other templates)
footer.php Site footer (included by other templates)
sidebar.php Primary sidebar
sidebar-page.php Page-specific sidebar

ACF Blocks (in views/blocks/):

Each block follows a consistent three-file pattern: block.json (registration) + {name}.php (template) + {name}.css (styles).

Block Purpose
accordion Collapsible content sections
boilerplate Starting template for new blocks (not registered)
button Single configurable button element
buttons Container that restricts children to button blocks
contact-info Contact information display
grid Flexible grid layout (restricts children to grid-cell)
grid-cell Individual grid item
homepage-hero Hero section for the front page
media-text Image/video with accompanying text
media-text-innerblocks Media-text with nested block support
page-children Displays child pages of the current page
section Container with background and width options

Components (in views/components/):

Component Purpose
nav-aux.php Auxiliary navigation bar (social links + search)
nav-main.php Primary navigation menu
nav-main__toggle.php Mobile menu toggle button
menu-items/ Recursive menu item rendering (index, has-children, single)

Partials (in views/partials/):

Partial Purpose
page-hero.php Page hero section
social-media.php Social media links

Icons (in views/icons/):

SVG icon partials for Facebook, Instagram, LinkedIn, Pinterest, Twitter, YouTube, and others. Each is a minimal PHP file that outputs an SVG element.

4. Styling

CSS is organized as a layered cascade, managed through Tailwind CSS v4:

styles/theme.css                 ← Entry point (imports everything below)
├── @import "tailwindcss"        ← Tailwind CSS v4 base
├── @import "./base/index.css"   ← Base styles barrel file
│   ├── break-out.css            ← Container break-out utilities
│   ├── colors.css               ← Color custom properties
│   ├── forms.css                ← Form element styles
│   ├── global.css               ← Global resets and base styles
│   ├── misc.css                 ← Miscellaneous utilities
│   ├── prose.css                ← Prose/typography styles
│   ├── skip-link.css            ← Accessibility skip link
│   └── typography.css           ← Typography scale and fonts
├── @import "./navigation/index.css"  ← Navigation barrel file
│   ├── nav-aux.css              ← Auxiliary nav
│   ├── nav-footer.css           ← Footer nav
│   ├── nav-functional.css       ← Functional nav styles
│   ├── nav-main-default.css     ← Default main nav
│   ├── nav-main-mega.css        ← Mega menu nav
│   ├── nav-mobile-accordion.css ← Accordion mobile nav
│   └── nav-mobile-sliding.css  ← Sliding mobile nav
├── @import "./fonts/lineicons.css"   ← Icon font (600+ glyphs)
├── @import "./base/break-out.css"    ← Break-out utilities (loaded after nav)
├── @import "./components/index.css"  ← Components barrel file
│   ├── breadcrumbs.css          ← Breadcrumbs
│   ├── pagination.css           ← Pagination
│   ├── post-list.css            ← Post listings
│   ├── sidebar.css              ← Sidebar
│   ├── site-footer.css          ← Footer
│   └── site-header.css          ← Header
└── @import "./blocks/index.css"      ← Block styles barrel file
    ├── buttons.css               ← Button styles with CSS custom properties
    └── core.css                  ← Core block overrides

Why this structure? The barrel files (index.css) make it easy to add or remove stylesheets without modifying theme.css. Navigation styles are grouped because you typically only use one variant (default vs mega menu, accordion vs sliding mobile). Block-specific CSS lives alongside each block in views/blocks/ and is auto-loaded by WordPress when the block renders.

Important: There is no tailwind.config.js. Tailwind CSS v4 uses CSS-first configuration via @import "tailwindcss" and @plugin directives in theme.css. Custom colors, fonts, and spacing are defined in theme.json and styles/base/colors.css.

5. Client Scripts

JavaScript modules loaded via WordPress's wp_enqueue_script_module() API (requires WordPress 6.5+):

static/js/theme.js (entry point for frontend)
├── Navigation.js    → Mobile menu, sliding viewport, keyboard nav
├── backToTop.js      → BackToTopButton custom element
├── button.js         → ButtonComponent custom element (<x-button>)
├── GetHeaderHeight.js → Sets --header-height CSS variable
└── TagExternalLinks.js → Adds target="_blank" rel="noopener" to external links

static/js/admin.js (entry point for editor)
└── button.js         → ButtonComponent for editor context

How script modules work: WordPress's wp_enqueue_script_module() creates proper ES module dependencies. The Enqueue class registers basicwp-theme (theme.js) as a root module, and basicwp-button (button.js) declares a dependency on it. This means button.js won't load until theme.js has loaded — no more manual script ordering.

Passive event listener polyfill: theme.js includes a polyfill that makes scroll, touch, and mouse event listeners passive by default. This improves scrolling performance without requiring addEventListener(..., { passive: true }) on every listener.

Custom elements: The ButtonComponent (<x-button>) and BackToTopButton (<back-to-top>) are Web Components registered via customElements.define(). They accept attributes for styling, URL, target, and behavior.

6. Data Layer

ACF field group JSON files in the acf/ directory:

File Block/Feature
group_5f7f85a2a3e13.json Accordion block fields
group_5fd3e006e5da5.json Global Fields (site-wide contact, social, footer settings)
group_600f5a9e242c3.json Grid block fields
group_60106ed700da3.json Button block fields
group_60bfb84ae973c.json Media Text block fields
group_60bfdb328901d.json Section block fields
group_6261bc658dd80.json Homepage Hero and Section fields
group_645e51f721207.json Grid Cell block fields
group_645e7cf448e66.json Contact Info block fields

Why JSON sync? The ACF class in class-acf.php sets custom save/load paths so that field groups created in the WordPress admin are automatically saved as JSON files. This means:

  • Field group configurations are version-controlled in Git
  • Field groups survive database resets
  • Multiple environments stay in sync
  • You can edit field groups in code or in the admin UI

7. Infrastructure

Build, CI/CD, and configuration files that support development and deployment:

File Purpose
bin/.build.js Production build script — compiles Tailwind CSS with --optimize
bin/.watch.js Development server — BrowserSync with live reload
bin/.utils.js Shared utilities for build scripts (tailwindToCSS, debounce)
.github/workflows/wpengine.yml Deploys to WP Engine via rsync on manual trigger
.github/workflows/phpcs.yml Runs PHP CodeSniffer on pull requests
.github/workflows/todos.yml Syncs code TODOs to GitHub Issues
package.json Node dependencies and build/watch scripts
composer.json PHP dependencies (PHP_CodeSniffer, WordPress coding standards)
theme.json WordPress block editor configuration (colors, fonts, spacing)
.phpcs.xml PHP CodeSniffer ruleset
.env.example Environment variable template (LOCALHOST_URL, BROWSERSYNC_PORT)
playwright.config.js Playwright accessibility test configuration
tests/site-a11y.spec.js Accessibility test suite using @axe-core/playwright

Namespace Conventions

The project uses several naming conventions that can be confusing at first:

Convention Value Where Used
PHP namespace BasicWP All PHP files use namespace BasicWP;
Text domain basicwp WordPress translation functions (__() , _e())
Block category vdi-blocks Groups custom blocks in the editor (defined in helpers.php::blockCategories())
Script module IDs basicwp-theme, basicwp-button, basicwp-admin JavaScript module registration in class-enqueue.php
WP Engine folder vdi-v5 Deployment target in .github/workflows/wpengine.yml
Git repo name VDI-Starter-v5 The repository and theme directory name

The story: "VDI" is Vincent Design Inc., the agency. "BasicWP" was the original internal code name. "vdi-blocks" and "vdi-v5" are deployment-facing names that align with the client's branding. They all refer to the same theme — just used in different contexts.

Global Variables

Two global variables are defined in helpers.php:

global $theme, $views;
$theme = get_template_directory();    // e.g., /var/www/wp-content/themes/VDI-Starter-v5
$views = $theme . '/views';           // e.g., /var/www/wp-content/themes/VDI-Starter-v5/views

$theme — Absolute path to the theme directory. Used when including files that need the full server path.

$views — Absolute path to the views directory. Used by MenuItems::render() to include navigation templates:

include $views . '/components/menu-items/index.php';

These are available everywhere because helpers.php is loaded early via the glob autoload in functions.php.

WordPress Hooks Cleanup

The init() function in hooks.php runs on every page load at priority 1. It performs aggressive cleanup of default WordPress output for performance and security reasons:

What gets removed:

What Why
Emoji detection scripts & styles Most sites don't use WordPress emojis; they add ~10KB to every page
wp-block-library styles Theme provides its own block styles; core defaults add ~100KB
global-styles & core-block-styles Theme overrides these via theme.json and custom CSS
core-block-supports Duplicate of styling already handled by the theme
WordPress generator meta tag Security — hides WordPress version from page source
RSD link Rarely used XML-RPC discovery
WLW manifest Windows Live Writer support (deprecated)
Shortlink Removes <link rel="shortlink"> from head
REST API link in head The API still works; only the discoverable link is removed
oEmbed discovery links Removes auto-embed discovery from head
Canonical URL Theme handles SEO via The SEO Framework plugin
DNS prefetch hints Removes unused <link rel="dns-prefetch"> tags
XML-RPC Disabled via xmlrpc_enabled filter — prevents brute-force attacks
Intrinsic image sizes Prevents WordPress from adding width/height to img tags
Auto sizes Prevents automatic sizes attribute on images

What gets added:

Feature Why
post-thumbnails Featured image support
title-tag WordPress manages <title> tag
html5 (caption, comment-form, comment-list, gallery, search-form, script, style) Modern HTML5 markup
align-wide Wide/full alignment for blocks
editor-styles Theme styles appear in the block editor
responsive-embeds Embeds respond to container width
customize-selective-refresh-widgets Widget changes update without full page reload
SVG upload support Allows SVG files in the media library

Important: This cleanup is aggressive. If a plugin requires one of the removed features (like oEmbed discovery or XML-RPC), you'll need to comment out the corresponding remove_action line in hooks.php.

Enqueue System

The Enqueue class (in class-enqueue.php) manages all asset loading:

Frontend (enqFEAssets())

Asset Method Notes
static/dist/theme.css wp_enqueue_style() Compiled Tailwind CSS, cache-busted with filemtime()
Raleway font wp_enqueue_style() Google Fonts with preconnect hint
basicwp-theme (theme.js) wp_enqueue_script_module() Frontend entry point
basicwp-button (button.js) wp_enqueue_script_module() Depends on basicwp-theme
jQuery wp_enqueue_script() Needed by downstream scripts; modules can't depend on classic scripts

Admin (enqBEAssets())

Asset Method Notes
Raleway font wp_enqueue_style() Same Google Fonts
styles/backend/admin.css wp_enqueue_style() Admin-specific overrides
basicwp-admin (admin.js) wp_enqueue_script_module() Admin entry point
basicwp-button (button.js) wp_enqueue_script_module() Depends on basicwp-admin

Block Editor (enqEditorAssets())

Asset Method Notes
Raleway font wp_enqueue_style() Same Google Fonts
styles/backend/editor.css wp_enqueue_style() Editor-specific styles, scoped to block editor

Cache busting: All enqueued files use filemtime() as the version number. This means the browser cache is automatically busted whenever a file changes — no manual version bumps needed.

Script modules: The theme uses wp_enqueue_script_module() (WordPress 6.5+) instead of traditional wp_enqueue_script() for frontend and admin JavaScript. This creates proper ES module dependencies where basicwp-button won't load until basicwp-theme has loaded.

theme.json Design System

The theme.json file (WordPress block editor v3 schema) defines the design system for both the block editor and the frontend:

Colors

Colors are defined as CSS custom properties and mapped to WordPress editor slugs:

Editor Slug CSS Variable Purpose
black #000 Pure black
white #fff Pure white
theme-bg var(--color-background) Page background
theme-text var(--color-text) Body text
theme-primary var(--color-primary) Primary brand color
theme-secondary var(--color-secondary) Secondary brand color
theme-bodylinks var(--color-bodylinks) Body link color
theme-footerlinks var(--color-footlinks) Footer link color
theme-success var(--color-success) Success/positive
theme-warning var(--color-warning) Warning/caution
theme-danger var(--color-danger) Danger/error
theme-info var(--color-info) Informational

The actual color values for the CSS variables are defined in styles/base/colors.css.

Typography

One font family (var(--font-sans)) and 15 size presets:

Slug Variable Typical Use
base var(--text-base) Body text
text-14px var(--text-14px) Small text
text-16px var(--text-16px) Standard text
text-18px var(--text-18px) Large text
text-20px var(--text-20px) Subheadings
text-22px var(--text-22px) H4
text-25px var(--text-25px) H3
text-30px var(--text-30px) H2
text-35px var(--text-35px) Large heading
text-38px var(--text-38px) H1 (small)
text-40px var(--text-40px) H1
text-45px var(--text-45px) Hero heading
text-50px var(--text-50px) Large hero
text-70px var(--text-70px) Display size
text-75px var(--text-75px) Maximum display

Layout

Property Value Meaning
contentSize 100% Default content width (full-width by default)
wideSize 1536px Wide-alignment max width

Spacing

Available units: px, em, rem, vh, vw, %

Global Styles

Property Value
Background var(--wp--preset--color--background)
Text color var(--wp--preset--color--text)
Link color var(--wp--preset--color--theme-bodylinks)
Font family var(--wp--preset--font-family--theme-sans)
Line height 1.5

Why theme.json matters: Changes to this file immediately affect the block editor UI — colors appear in the palette, font sizes in the typography controls, and spacing in the spacing panel. This is the single source of truth for the design system, and CSS custom properties cascade from here into the frontend styles.