🔵 other: Rename project and publish
This commit is contained in:
+115
-98
@@ -1,44 +1,61 @@
|
||||
# Architecture
|
||||
|
||||
A deep dive into how VDI-Starter-v5 is organized, how it boots, and the conventions it uses.
|
||||
A deep dive into how SoloFrame Evo is organized, how it boots, and the conventions it uses.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Bootstrap Flow](#bootstrap-flow)
|
||||
- [Architectural Layers](#architectural-layers)
|
||||
- [Namespace Conventions](#namespace-conventions)
|
||||
- [Global Variables](#global-variables)
|
||||
- [WordPress Hooks Cleanup](#wordpress-hooks-cleanup)
|
||||
- [Enqueue System](#enqueue-system)
|
||||
- [theme.json Design System](#themjson-design-system)
|
||||
- [Architecture](#architecture)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Bootstrap Flow](#bootstrap-flow)
|
||||
- [Architectural Layers](#architectural-layers)
|
||||
- [1. Entry Layer](#1-entry-layer)
|
||||
- [2. Service Layer](#2-service-layer)
|
||||
- [3. UI Templates and Components](#3-ui-templates-and-components)
|
||||
- [4. Styling](#4-styling)
|
||||
- [5. Client Scripts](#5-client-scripts)
|
||||
- [6. Data Layer](#6-data-layer)
|
||||
- [7. Infrastructure](#7-infrastructure)
|
||||
- [Namespace Conventions](#namespace-conventions)
|
||||
- [Global Variables](#global-variables)
|
||||
- [WordPress Hooks Cleanup](#wordpress-hooks-cleanup)
|
||||
- [Enqueue System](#enqueue-system)
|
||||
- [Frontend (`enqFEAssets()`)](#frontend-enqfeassets)
|
||||
- [Admin (`enqBEAssets()`)](#admin-enqbeassets)
|
||||
- [Block Editor (`enqEditorAssets()`)](#block-editor-enqeditorassets)
|
||||
- [theme.json Design System](#themejson-design-system)
|
||||
- [Colors](#colors)
|
||||
- [Typography](#typography)
|
||||
- [Layout](#layout)
|
||||
- [Spacing](#spacing)
|
||||
- [Global Styles](#global-styles)
|
||||
|
||||
## 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:
|
||||
When WordPress loads a theme, it starts with `style.css` (for theme metadata) and `functions.php` (for logic). Here's exactly what happens in SoloFrame Evo:
|
||||
|
||||
```
|
||||
```plain
|
||||
WordPress loads the theme
|
||||
│
|
||||
├─ style.css → Theme declaration (name, description, version)
|
||||
│
|
||||
└─ functions.php → Entry point
|
||||
│
|
||||
├─ namespace BasicWP
|
||||
├─ namespace SoloFrameEvo
|
||||
│
|
||||
├─ 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
|
||||
├─ 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)
|
||||
└─ regACFBlocks() → Registers ACF blocks (init hook, priority 5)
|
||||
└─ Scans views/blocks/*/block.json (skips 'boilerplate')
|
||||
```
|
||||
|
||||
@@ -55,7 +72,7 @@ The theme is organized into seven distinct layers, each with a clear responsibil
|
||||
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 |
|
||||
|
||||
@@ -66,7 +83,7 @@ The two files WordPress needs to recognize the theme:
|
||||
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 |
|
||||
@@ -86,7 +103,7 @@ 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 |
|
||||
@@ -103,7 +120,7 @@ WordPress template hierarchy files, ACF blocks, reusable components, and icons:
|
||||
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 |
|
||||
@@ -120,7 +137,7 @@ Each block follows a consistent three-file pattern: `block.json` (registration)
|
||||
**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 |
|
||||
@@ -129,7 +146,7 @@ Each block follows a consistent three-file pattern: `block.json` (registration)
|
||||
**Partials (in `views/partials/`):**
|
||||
|
||||
| Partial | Purpose |
|
||||
|---------|---------|
|
||||
| --------- | --------- |
|
||||
| `page-hero.php` | Page hero section |
|
||||
| `social-media.php` | Social media links |
|
||||
|
||||
@@ -141,38 +158,38 @@ SVG icon partials for Facebook, Instagram, LinkedIn, Pinterest, Twitter, YouTube
|
||||
|
||||
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
|
||||
```plain
|
||||
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.
|
||||
@@ -183,19 +200,19 @@ styles/theme.css ← Entry point (imports everything below)
|
||||
|
||||
JavaScript modules loaded via WordPress's `wp_enqueue_script_module()` API (requires WordPress 6.5+):
|
||||
|
||||
```
|
||||
```plain
|
||||
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
|
||||
├── 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
|
||||
└── 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.
|
||||
**How script modules work:** WordPress's `wp_enqueue_script_module()` creates proper ES module dependencies. The `Enqueue` class registers `sf-evo-theme` (theme.js) as a root module, and `sf-evo-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.
|
||||
|
||||
@@ -203,10 +220,10 @@ static/js/admin.js (entry point for editor)
|
||||
|
||||
### 6. Data Layer
|
||||
|
||||
ACF field group JSON files in the `acf/` directory:
|
||||
SCF/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 |
|
||||
@@ -218,6 +235,7 @@ ACF field group JSON files in the `acf/` directory:
|
||||
| `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
|
||||
@@ -228,7 +246,7 @@ ACF field group JSON files in the `acf/` directory:
|
||||
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) |
|
||||
@@ -248,15 +266,13 @@ Build, CI/CD, and configuration files that support development and deployment:
|
||||
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.
|
||||
| ----------- | ------- | ----------- |
|
||||
| PHP namespace | `SoloFrameEvo` | All PHP files use `namespace SoloFrameEvo;` |
|
||||
| Text domain | `sf-evo` | WordPress translation functions (`__()` , `_e()`) |
|
||||
| Block category | `sf-blocks` | Groups custom blocks in the editor (defined in `helpers.php::blockCategories()`) |
|
||||
| Script module IDs | `sf-evo-theme`, `sf-evo-button`, `sf-evo-admin` | JavaScript module registration in `class-enqueue.php` |
|
||||
| WP Engine folder | `soloframe-evo` | Deployment target in `.github/workflows/wpengine.yml` |
|
||||
| Git repo name | `SoloFrame-Evo` | The repository and theme directory name |
|
||||
|
||||
## Global Variables
|
||||
|
||||
@@ -264,13 +280,14 @@ Two global variables are defined in `helpers.php`:
|
||||
|
||||
```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 = get_template_directory(); // e.g., /var/www/wp-content/themes/SoloFrame Evo
|
||||
$views = $theme . '/views'; // e.g., /var/www/wp-content/themes/SoloFrame Evo/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:
|
||||
|
||||
```php
|
||||
include $views . '/components/menu-items/index.php';
|
||||
```
|
||||
@@ -284,7 +301,7 @@ The `init()` function in `hooks.php` runs on every page load at priority 1. It p
|
||||
**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 |
|
||||
@@ -304,7 +321,7 @@ The `init()` function in `hooks.php` runs on every page load at priority 1. It p
|
||||
**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 |
|
||||
@@ -323,32 +340,32 @@ 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` |
|
||||
| `sf-evo-theme` (theme.js) | `wp_enqueue_script_module()` | Frontend entry point |
|
||||
| `sf-evo-button` (button.js) | `wp_enqueue_script_module()` | Depends on `sf-evo-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` |
|
||||
| `sf-evo-admin` (admin.js) | `wp_enqueue_script_module()` | Admin entry point |
|
||||
| `sf-evo-button` (button.js) | `wp_enqueue_script_module()` | Depends on `sf-evo-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.
|
||||
**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 `sf-evo-button` won't load until `sf-evo-theme` has loaded.
|
||||
|
||||
## theme.json Design System
|
||||
|
||||
@@ -359,7 +376,7 @@ The `theme.json` file (WordPress block editor v3 schema) defines the design syst
|
||||
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 |
|
||||
@@ -380,7 +397,7 @@ The actual color values for the CSS variables are defined in `styles/base/colors
|
||||
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 |
|
||||
@@ -400,7 +417,7 @@ One font family (`var(--font-sans)`) and 15 size presets:
|
||||
### Layout
|
||||
|
||||
| Property | Value | Meaning |
|
||||
|----------|-------|---------|
|
||||
| ---------- | ------- | --------- |
|
||||
| `contentSize` | `100%` | Default content width (full-width by default) |
|
||||
| `wideSize` | `1536px` | Wide-alignment max width |
|
||||
|
||||
@@ -411,11 +428,11 @@ 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.
|
||||
**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.
|
||||
|
||||
+100
-76
@@ -1,40 +1,55 @@
|
||||
# Creating Blocks in VDI-Starter-v5
|
||||
# Creating Blocks in SoloFrame Evo
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [How Block Registration Works](#how-block-registration-works)
|
||||
- [Block Anatomy: The Three-File Pattern](#block-anatomy-the-three-file-pattern)
|
||||
- [block.json -- The Registration Manifest](#blockjson----the-registration-manifest)
|
||||
- [The PHP Template -- Rendering the Block](#the-php-template----rendering-the-block)
|
||||
- [The CSS File -- Scoped Styles](#the-css-file----scoped-styles)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [blockWrapperAttributes()](#blockwrapperattributes)
|
||||
- [getFieldValue()](#getfieldvalue)
|
||||
- [escEmbeds()](#escembeds)
|
||||
- [ACF Field Groups](#acf-field-groups)
|
||||
- [Parent-Child Block Patterns (InnerBlocks)](#parent-child-block-patterns-innerblocks)
|
||||
- [Tailwind CSS in Blocks](#tailwind-css-in-blocks)
|
||||
- [Step-by-Step: Creating a New Block](#step-by-step-creating-a-new-block)
|
||||
- [1. Create the Block Directory](#1-create-the-block-directory)
|
||||
- [2. Create block.json](#2-create-blockjson)
|
||||
- [3. Create the PHP Template](#3-create-the-php-template)
|
||||
- [4. Create the CSS File](#4-create-the-css-file)
|
||||
- [5. Create ACF Field Groups in WordPress Admin](#5-create-acf-field-groups-in-wordpress-admin)
|
||||
- [6. Build and Verify](#6-build-and-verify)
|
||||
- [Real-World Examples from This Theme](#real-world-examples-from-this-theme)
|
||||
- [Simple Block: Homepage Hero](#simple-block-homepage-hero)
|
||||
- [Parent Block with InnerBlocks: Section](#parent-block-with-innerblocks-section)
|
||||
- [Restricted Parent Block: Buttons](#restricted-parent-block-buttons)
|
||||
- [Dynamic Parent Block: Grid](#dynamic-parent-block-grid)
|
||||
- [Block Using Global Fields: Contact Info](#block-using-global-fields-contact-info)
|
||||
- [Common Pitfalls and Best Practices](#common-pitfalls-and-best-practices)
|
||||
- [Creating Blocks in SoloFrame Evo](#creating-blocks-in-soloframe-evo)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Overview](#overview)
|
||||
- [How Block Registration Works](#how-block-registration-works)
|
||||
- [Block Anatomy: The Three-File Pattern](#block-anatomy-the-three-file-pattern)
|
||||
- [block.json -- The Registration Manifest](#blockjson----the-registration-manifest)
|
||||
- [The PHP Template -- Rendering the Block](#the-php-template----rendering-the-block)
|
||||
- [The CSS File -- Scoped Styles](#the-css-file----scoped-styles)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [blockWrapperAttributes()](#blockwrapperattributes)
|
||||
- [getFieldValue()](#getfieldvalue)
|
||||
- [escEmbeds()](#escembeds)
|
||||
- [SCF/ACF Field Groups](#scfacf-field-groups)
|
||||
- [Creating a Field Group](#creating-a-field-group)
|
||||
- [JSON Sync](#json-sync)
|
||||
- [Parent-Child Block Patterns (InnerBlocks)](#parent-child-block-patterns-innerblocks)
|
||||
- [Basic InnerBlocks](#basic-innerblocks)
|
||||
- [Restricted InnerBlocks](#restricted-innerblocks)
|
||||
- [Enabling InnerBlocks in block.json](#enabling-innerblocks-in-blockjson)
|
||||
- [Adding Classes to InnerBlocks](#adding-classes-to-innerblocks)
|
||||
- [Tailwind CSS in Blocks](#tailwind-css-in-blocks)
|
||||
- [How Tailwind is Set Up](#how-tailwind-is-set-up)
|
||||
- [Using Tailwind Classes in Blocks](#using-tailwind-classes-in-blocks)
|
||||
- [Whitelisting Editor-Only Classes](#whitelisting-editor-only-classes)
|
||||
- [Block-Specific CSS Files](#block-specific-css-files)
|
||||
- [Step-by-Step: Creating a New Block](#step-by-step-creating-a-new-block)
|
||||
- [1. Create the Block Directory](#1-create-the-block-directory)
|
||||
- [2. Create block.json](#2-create-blockjson)
|
||||
- [3. Create the PHP Template](#3-create-the-php-template)
|
||||
- [4. Create the CSS File](#4-create-the-css-file)
|
||||
- [5. Create SCF/ACF Field Groups in WordPress Admin](#5-create-scfacf-field-groups-in-wordpress-admin)
|
||||
- [6. Build and Verify](#6-build-and-verify)
|
||||
- [Real-World Examples from This Theme](#real-world-examples-from-this-theme)
|
||||
- [Simple Block: Homepage Hero](#simple-block-homepage-hero)
|
||||
- [Parent Block with InnerBlocks: Section](#parent-block-with-innerblocks-section)
|
||||
- [Restricted Parent Block: Buttons](#restricted-parent-block-buttons)
|
||||
- [Dynamic Parent Block: Grid](#dynamic-parent-block-grid)
|
||||
- [Block Using Global Fields: Contact Info](#block-using-global-fields-contact-info)
|
||||
- [Common Pitfalls and Best Practices](#common-pitfalls-and-best-practices)
|
||||
- [Do](#do)
|
||||
- [Do Not](#do-not)
|
||||
- [Debugging Tips](#debugging-tips)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
VDI-Starter-v5 uses Advanced Custom Fields (ACF) blocks to build page content. ACF blocks are a type of WordPress Gutenberg block where the editing interface comes from ACF field groups and the rendering is handled by a PHP template (instead of React). This approach lets you build rich, structured content blocks using familiar PHP templating and Tailwind CSS, without writing JavaScript.
|
||||
SoloFrame Evo uses Secure Custom Fields (SCF) or Advanced Custom Fields (ACF) blocks to build page content. SCF/ACF blocks are a type of WordPress Gutenberg block where the editing interface comes from SCF/ACF field groups and the rendering is handled by a PHP template (instead of React). This approach lets you build rich, structured content blocks using familiar PHP templating and Tailwind CSS, without writing JavaScript.
|
||||
|
||||
Every block in this theme follows the same three-file pattern inside `views/blocks/{block-name}/`, and new blocks are automatically discovered and registered -- no manual registration required.
|
||||
|
||||
@@ -74,9 +89,9 @@ Here is what happens:
|
||||
|
||||
## Block Anatomy: The Three-File Pattern
|
||||
|
||||
Every ACF block in this theme consists of exactly three files inside `views/blocks/{block-name}/`:
|
||||
Every SCF/ACF block in this theme consists of exactly three files inside `views/blocks/{block-name}/`:
|
||||
|
||||
```
|
||||
```plain
|
||||
views/blocks/
|
||||
boilerplate/ <-- Template for creating new blocks (not registered)
|
||||
block.json
|
||||
@@ -97,15 +112,15 @@ The naming convention is consistent: the directory name, the PHP file, and the C
|
||||
|
||||
### block.json -- The Registration Manifest
|
||||
|
||||
The `block.json` file tells WordPress and ACF everything they need to know about the block. Here is the boilerplate version:
|
||||
The `block.json` file tells WordPress and SCF/ACF everything they need to know about the block. Here is the boilerplate version:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "acf/boilerplate",
|
||||
"title": "Block Boilerplate",
|
||||
"description": "Boilerplate code to create ACF blocks.",
|
||||
"description": "Boilerplate code to create SCF/ACF blocks.",
|
||||
"style": ["file:./boilerplate.css"],
|
||||
"category": "vdi-blocks",
|
||||
"category": "sf-blocks",
|
||||
"icon": "block-default",
|
||||
"keywords": ["boilerplate"],
|
||||
"acf": {
|
||||
@@ -127,13 +142,13 @@ The `block.json` file tells WordPress and ACF everything they need to know about
|
||||
**Field-by-field explanation:**
|
||||
|
||||
| Field | Purpose |
|
||||
|---|---|
|
||||
| --- | --- |
|
||||
| `name` | The unique block identifier. **Must be prefixed with `acf/`** for ACF blocks. This becomes the machine name WordPress uses internally (e.g., `acf/testimonial`). |
|
||||
| `title` | The human-readable name shown in the block editor inserter (e.g., "Testimonial"). |
|
||||
| `description` | A short description shown in the block editor to help editors understand what the block does. |
|
||||
| `style` | An array of CSS files to load when this block renders. Use the `file:./` prefix for block-relative paths. WordPress only loads these stylesheets when the block is actually present on the page. |
|
||||
| `category` | Determines which section of the inserter the block appears under. **Always use `vdi-blocks`** in this theme -- this is the custom category registered in `helpers.php` that groups all theme blocks together under "VDI Custom Blocks". |
|
||||
| `icon` | A Dashicon name (without the `dashicons-` prefix) shown next to the block in the inserter. Browse available icons at https://developer.wordpress.org/resource/dashicons/. |
|
||||
| `category` | Determines which section of the inserter the block appears under. **Always use `sf-blocks`** in this theme -- this is the custom category registered in `helpers.php` that groups all theme blocks together under "VDI Custom Blocks". |
|
||||
| `icon` | A Dashicon name (without the `dashicons-` prefix) shown next to the block in the inserter. Browse available icons at [DashIcons](https://developer.wordpress.org/resource/dashicons/). |
|
||||
| `keywords` | Additional search terms that help editors find the block in the inserter. For example, a "Testimonial" block might include `["testimonial", "quote", "review"]`. |
|
||||
| `acf.mode` | Controls how the block appears in the editor. `preview` shows the rendered block output; `edit` shows the ACF field inputs directly. Most blocks use `preview`. |
|
||||
| `acf.renderTemplate` | The PHP file that renders the block on the frontend and in preview mode. This filename must match the actual file in the directory. |
|
||||
@@ -150,10 +165,10 @@ The `block.json` file tells WordPress and ACF everything they need to know about
|
||||
```json
|
||||
{
|
||||
"name": "acf/buttons",
|
||||
"title": "Buttons (VDI)",
|
||||
"title": "Buttons",
|
||||
"description": "A button or group of buttons.",
|
||||
"allowedBlocks": ["acf/button"],
|
||||
"category": "vdi-blocks",
|
||||
"category": "sf-blocks",
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -169,10 +184,10 @@ The PHP template is responsible for outputting the block's HTML. Every template
|
||||
*
|
||||
* This is the template for building your own custom blocks.
|
||||
*
|
||||
* @package BasicWP
|
||||
* @package SoloFrameEvo
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
namespace SoloFrameEvo;
|
||||
|
||||
$classes = 'boilerplate';
|
||||
|
||||
@@ -190,7 +205,7 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
|
||||
**Key elements explained:**
|
||||
|
||||
1. **`namespace BasicWP;`** -- Every block template must declare this namespace. It gives you access to the theme's helper functions (`blockWrapperAttributes`, `getFieldValue`, etc.) without needing fully-qualified class names.
|
||||
1. **`namespace SoloFrameEvo;`** -- Every block template must declare this namespace. It gives you access to the theme's helper functions (`blockWrapperAttributes`, `getFieldValue`, etc.) without needing fully-qualified class names.
|
||||
|
||||
2. **`$is_preview`** -- This is a WordPress global variable that is `true` when the block is being rendered inside the block editor, and `false` on the frontend. You can use it to conditionally show editor-only content or adjust markup for the editor.
|
||||
|
||||
@@ -214,7 +229,7 @@ The CSS filename must match the block slug and be referenced in `block.json` usi
|
||||
"style": ["file:./testimonial.css"]
|
||||
```
|
||||
|
||||
You can use Tailwind utility classes directly in your PHP templates (e.g., `class="flex gap-4 p-6"`), and they will work as long as the Tailwind build process can detect them. For complex or block-specific styles that are not expressible as utility classes, write them in the block's CSS file using the BEM-like naming convention:
|
||||
You can use Tailwind utility classes directly in your PHP templates (e.g., `class="flex gap-4 p-6"`), and they will work as long as the Tailwind build process can detect them. For complex or block-specific styles that are not expressible as utility classes, write them in the block's CSS file. You can also use Tailwind's `@apply` directive in these CSS files to compose utility classes into reusable styles:
|
||||
|
||||
```css
|
||||
/* testimonial.css */
|
||||
@@ -238,7 +253,7 @@ The file can be empty initially and filled in as needed.
|
||||
|
||||
## Helper Functions
|
||||
|
||||
The theme provides several helper functions in `lib/helpers.php`, all under the `BasicWP` namespace. Because every block template declares `namespace BasicWP;`, you can call these functions directly without any prefix.
|
||||
The theme provides several helper functions in `lib/helpers.php`, all under the `SoloFrameEvo` namespace. Because every block template declares `namespace SoloFrameEvo;`, you can call these functions directly without any prefix.
|
||||
|
||||
### blockWrapperAttributes()
|
||||
|
||||
@@ -249,12 +264,14 @@ function blockWrapperAttributes( $classes, $is_preview )
|
||||
**Purpose:** Generates the HTML attributes string for a block's root element, handling the difference between the editor and the frontend.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `$classes` (string) -- A space-separated list of CSS class names to apply to the block wrapper.
|
||||
- `$is_preview` (bool) -- Whether the block is being rendered in the editor. Always pass the global `$is_preview` variable.
|
||||
|
||||
**Returns:** A string of HTML attributes ready to echo inside an HTML tag.
|
||||
|
||||
**How it works:**
|
||||
|
||||
- When `$is_preview` is `true` (in the editor), it returns `class="my-class"`. This is a simplified output that avoids rendering issues caused by WordPress's `get_block_wrapper_attributes()` in the editor context.
|
||||
- When `$is_preview` is `false` (on the frontend), it calls WordPress's `get_block_wrapper_attributes()` with your classes merged in, producing the full set of attributes including alignment classes, anchor IDs, custom class names from the editor, and more.
|
||||
|
||||
@@ -280,6 +297,7 @@ function getFieldValue( $field_path )
|
||||
**Purpose:** Retrieves nested values from ACF option fields (Global Fields) using dot notation.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `$field_path` (string) -- A dot-notated path to the value. For example, `'contact_info.phone'` retrieves the `phone` subfield from the `contact_info` options page field.
|
||||
|
||||
**Returns:** The value at the specified path, or an empty string if the path does not exist.
|
||||
@@ -314,7 +332,7 @@ echo wp_kses( $video_embed_html, escEmbeds() );
|
||||
|
||||
---
|
||||
|
||||
## ACF Field Groups
|
||||
## SCF/ACF Field Groups
|
||||
|
||||
After creating your block's three files, you need to create an ACF field group in the WordPress admin. This defines the fields that editors fill in when editing the block.
|
||||
|
||||
@@ -331,12 +349,12 @@ After creating your block's three files, you need to create an ACF field group i
|
||||
- **Repeater** -- Repeatable groups of fields (for lists, slides, etc.)
|
||||
- **Link** -- URL + title + target picker
|
||||
- **WYSIWYG** -- Rich text editor
|
||||
4. Set the **location rule** to: **Block > is equal to > [Your Block Name]**. This tells ACF to show these fields when editing your block.
|
||||
4. Set the **location rule** to: **Block > is equal to > [Your Block Name]**. This tells SCF/ACF to show these fields when editing your block.
|
||||
5. Click **Save** or **Publish**.
|
||||
|
||||
### JSON Sync
|
||||
|
||||
The theme's `ACF` class (in `lib/class-acf.php`) configures custom save and load paths for ACF JSON:
|
||||
The theme's `ACF` class (in `lib/class-acf.php`) configures custom save and load paths for SCF/ACF JSON:
|
||||
|
||||
```php
|
||||
class ACF {
|
||||
@@ -359,6 +377,7 @@ class ACF {
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
- When you save a field group in the admin, ACF writes a JSON file to the `acf/` directory in the theme root.
|
||||
- When ACF loads field groups, it reads from the same `acf/` directory.
|
||||
- These JSON files are version-controlled, so field group configurations travel with the codebase and sync across environments.
|
||||
@@ -369,7 +388,7 @@ This means:
|
||||
|
||||
## Parent-Child Block Patterns (InnerBlocks)
|
||||
|
||||
Some blocks act as containers that hold other blocks. WordPress provides `<InnerBlocks />` for this purpose, and ACF blocks can use it too.
|
||||
Some blocks act as containers that hold other blocks. WordPress provides `<InnerBlocks />` for this purpose, and SCF/ACF blocks can use it too.
|
||||
|
||||
### Basic InnerBlocks
|
||||
|
||||
@@ -506,7 +525,7 @@ To prevent this, add editor-only classes to `whitelist.php`. This file contains
|
||||
<!-- ... more classes ... -->
|
||||
```
|
||||
|
||||
The whitelist is primarily used for grid and layout classes that the Grid block applies dynamically through ACF field values (since those class names are generated at runtime, not hardcoded in templates).
|
||||
The whitelist is primarily used for grid and layout classes that the Grid block applies dynamically through SCF/ACF field values (since those class names are generated at runtime, not hardcoded in templates).
|
||||
|
||||
### Block-Specific CSS Files
|
||||
|
||||
@@ -524,7 +543,7 @@ This walkthrough demonstrates creating a "Testimonial" block from scratch.
|
||||
|
||||
Create a new folder under `views/blocks/` using a lowercase, hyphenated slug:
|
||||
|
||||
```
|
||||
```plain
|
||||
views/blocks/testimonial/
|
||||
```
|
||||
|
||||
@@ -538,9 +557,9 @@ Create `views/blocks/testimonial/block.json`:
|
||||
{
|
||||
"name": "acf/testimonial",
|
||||
"title": "Testimonial",
|
||||
"description": "A customer testimonial with quote, name, and role.",
|
||||
"description": "A customer testimonial with quote, name, image, and role.",
|
||||
"style": ["file:./testimonial.css"],
|
||||
"category": "vdi-blocks",
|
||||
"category": "sf-blocks",
|
||||
"icon": "format-quote",
|
||||
"keywords": ["testimonial", "quote", "review"],
|
||||
"acf": {
|
||||
@@ -560,8 +579,9 @@ Create `views/blocks/testimonial/block.json`:
|
||||
```
|
||||
|
||||
**Checklist for `block.json`:**
|
||||
|
||||
- `name` starts with `acf/`
|
||||
- `category` is set to `vdi-blocks`
|
||||
- `category` is set to `sf-blocks`
|
||||
- `style` references the CSS file with the `file:./` prefix
|
||||
- `acf.renderTemplate` matches the PHP filename exactly
|
||||
- `supports.html` is `false` (ACF blocks should not support HTML editing)
|
||||
@@ -578,10 +598,10 @@ Create `views/blocks/testimonial/testimonial.php`:
|
||||
*
|
||||
* A customer testimonial with quote, name, and role.
|
||||
*
|
||||
* @package BasicWP
|
||||
* @package SoloFrameEvo
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
namespace SoloFrameEvo;
|
||||
|
||||
$classes = 'testimonial';
|
||||
$wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
@@ -618,14 +638,14 @@ $image = get_field( 'image' );
|
||||
```
|
||||
|
||||
**Checklist for the PHP template:**
|
||||
- Always start with `namespace BasicWP;`
|
||||
|
||||
- Always start with `namespace SoloFrameEvo;`
|
||||
- Always call `blockWrapperAttributes( $classes, $is_preview )` and assign it to `$wrapper`
|
||||
- Always echo `$wrapper` inside the root element with `wp_kses_post()`
|
||||
- Use `get_field()` to retrieve ACF field values
|
||||
- Use `getFieldValue()` for nested option fields
|
||||
- Escape all output: `wp_kses_post()` for HTML content, `esc_html()` for plain text, `esc_url()` for URLs, `esc_attr()` for HTML attributes
|
||||
- Use semantic HTML elements (`<section>`, `<blockquote>`, `<cite>`, etc.)
|
||||
- Follow BEM-like naming for CSS classes: `.block`, `.block__element`, `.block__element--modifier`
|
||||
|
||||
### 4. Create the CSS File
|
||||
|
||||
@@ -665,7 +685,7 @@ Create `views/blocks/testimonial/testimonial.css`. It can start empty or with ba
|
||||
|
||||
If you are using Tailwind utility classes in the PHP template, you may not need much custom CSS. The file still needs to exist and be referenced in `block.json` so WordPress can load it.
|
||||
|
||||
### 5. Create ACF Field Groups in WordPress Admin
|
||||
### 5. Create SCF/ACF Field Groups in WordPress Admin
|
||||
|
||||
1. Log in to the WordPress admin dashboard.
|
||||
2. Go to **Custom Fields > Add New**.
|
||||
@@ -673,7 +693,7 @@ If you are using Tailwind utility classes in the PHP template, you may not need
|
||||
4. Add the following fields:
|
||||
|
||||
| Field Label | Field Name | Field Type | Notes |
|
||||
|---|---|---|---|
|
||||
| --- | --- | --- | --- |
|
||||
| Quote | `quote` | Textarea | The testimonial text |
|
||||
| Name | `name` | Text | The customer's name |
|
||||
| Role | `role` | Text | The customer's role or title (optional) |
|
||||
@@ -695,7 +715,7 @@ npm run build
|
||||
Then verify the block appears in the editor:
|
||||
|
||||
1. Edit a page in the WordPress block editor.
|
||||
2. Open the inserter and look under **VDI Custom Blocks**.
|
||||
2. Open the inserter and look under **SoloFrame Custom Blocks**.
|
||||
3. You should see "Testimonial" with the quote icon.
|
||||
4. Insert the block and fill in the fields.
|
||||
5. Save and preview the page on the frontend to confirm rendering works correctly.
|
||||
@@ -709,7 +729,8 @@ Then verify the block appears in the editor:
|
||||
The Homepage Hero is a straightforward block that retrieves ACF fields and renders them with Tailwind classes. It does not use InnerBlocks.
|
||||
|
||||
**Key patterns:**
|
||||
- Retrieves multiple ACF fields with `get_field()`
|
||||
|
||||
- Retrieves multiple SCF/ACF fields with `get_field()`
|
||||
- Conditionally renders sections only when fields have values (`! empty( $heading )`)
|
||||
- Uses Tailwind classes extensively for layout and styling
|
||||
- Handles editor vs. frontend differences for link URLs
|
||||
@@ -740,9 +761,10 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
|
||||
### Parent Block with InnerBlocks: Section
|
||||
|
||||
The Section block is a container that wraps its child blocks with optional background styling. It demonstrates conditional rendering based on ACF fields.
|
||||
The Section block is a container that wraps its child blocks with optional background styling. It demonstrates conditional rendering based on SCF/ACF fields.
|
||||
|
||||
**Key patterns:**
|
||||
|
||||
- Builds CSS class strings dynamically based on field values
|
||||
- Builds inline `style` strings from field values
|
||||
- Conditionally renders an overlay div
|
||||
@@ -801,6 +823,7 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
The Buttons block is a container that only allows Button blocks as children. It enforces this restriction through both `block.json` and the template.
|
||||
|
||||
**Key patterns:**
|
||||
|
||||
- Uses `allowedBlocks` in `block.json` to restrict children to `acf/button`
|
||||
- Sets `"jsx": true` in `supports` to enable InnerBlocks
|
||||
- Passes Tailwind classes to InnerBlocks via the `className` prop
|
||||
@@ -835,6 +858,7 @@ Note: The Buttons block uses `esc_attr()` instead of `wp_kses_post()` for the wr
|
||||
The Grid block builds CSS classes dynamically from ACF field values (columns, breakpoints, gaps). This is a case where runtime-generated class names need to be whitelisted.
|
||||
|
||||
**Key patterns:**
|
||||
|
||||
- Dynamically constructs Tailwind class names from field values (e.g., `'grid-cols-' . get_field( 'columns' )`)
|
||||
- Uses `$block['anchor']` and `$block['className']` for editor-set attributes
|
||||
- These dynamic class names are added to `whitelist.php` so Tailwind includes them in the build
|
||||
@@ -873,27 +897,27 @@ Because the class names like `grid-cols-3` and `md:grid-cols-4` are generated at
|
||||
The Contact Info block demonstrates how to access ACF options page data (Global Fields) using `getFieldValue()`.
|
||||
|
||||
**Key patterns:**
|
||||
- Uses `get_field( 'contact_info', 'option' )` to retrieve the options page field group, then accesses sub-fields with array syntax
|
||||
- Alternatively, could use `getFieldValue( 'contact_info.phone' )` for the same result
|
||||
|
||||
- Uses `getFieldValue( 'contact_info.phone' )` to retrieve the options page field group, then accesses sub-fields with array syntax
|
||||
- Combines static content from Global Fields with dynamic InnerBlocks content (a contact form)
|
||||
|
||||
```php
|
||||
namespace BasicWP;
|
||||
namespace SoloFrameEvo;
|
||||
|
||||
$classes = 'contact-info';
|
||||
$wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
?>
|
||||
|
||||
<section <?php echo esc_attr( $wrapper ); ?>>
|
||||
<section <?php echo wp_kses_post( $wrapper ); ?>>
|
||||
<div class="flex flex-col lg:flex-row">
|
||||
<div class="w-full lg:w-1/2 p-6">
|
||||
<h2 class="text-2xl font-bold mb-4">Contact Information</h2>
|
||||
<p><?php echo wp_kses_post( get_field( 'contact_info', 'option' )['address'] ); ?></p>
|
||||
<p><a href="mailto:<?php echo esc_html( get_field( 'contact_info', 'option' )['email'] ); ?>">
|
||||
<?php echo esc_html( get_field( 'contact_info', 'option' )['email'] ); ?>
|
||||
<p><?php echo wp_kses_post( getFieldValue( 'contact_info.address' ) ); ?></p>
|
||||
<p><a href="mailto:<?php echo esc_html( getFieldValue( 'contact_info.email' ) ); ?>">
|
||||
<?php echo esc_html( getFieldValue( 'contact_info.email' ) ); ?>
|
||||
</a></p>
|
||||
<p><a href="tel:<?php echo esc_html( get_field( 'contact_info', 'option' )['phone'] ); ?>">
|
||||
<?php echo esc_html( get_field( 'contact_info', 'option' )['phone'] ); ?>
|
||||
<p><a href="tel:<?php echo esc_html( getFieldValue( 'contact_info.phone' ) ); ?>">
|
||||
<?php echo esc_html( getFieldValue( 'contact_info.phone' ) ); ?>
|
||||
</a></p>
|
||||
</div>
|
||||
|
||||
@@ -910,12 +934,12 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
|
||||
### Do
|
||||
|
||||
- **Always use `namespace BasicWP;`** at the top of every block PHP template. Without it, helper functions like `blockWrapperAttributes()` and `getFieldValue()` will not be available.
|
||||
- **Always use `namespace SoloFrameEvo;`** at the top of every block PHP template. Without it, helper functions like `blockWrapperAttributes()` and `getFieldValue()` will not be available.
|
||||
- **Always use `blockWrapperAttributes()`** for the root element's attributes. Never call `get_block_wrapper_attributes()` directly.
|
||||
- **Always escape output.** Use `wp_kses_post()` for HTML content, `esc_html()` for plain text, `esc_url()` for URLs, and `esc_attr()` for HTML attribute values.
|
||||
- **Always set `category` to `vdi-blocks`** in `block.json` so your block appears under "VDI Custom Blocks" in the editor.
|
||||
- **Always set `category` to `sf-blocks`** in `block.json` so your block appears under "VDI Custom Blocks" in the editor.
|
||||
- **Always prefix `name` with `acf/`** in `block.json` (e.g., `"acf/testimonial"`, not just `"testimonial"`).
|
||||
- **Always set `supports.html` to `false`** in `block.json` for ACF blocks. ACF blocks use PHP templates, not HTML editing.
|
||||
- **Always set `supports.html` to `false`** in `block.json` for SCF/ACF blocks. SCF/ACF blocks use PHP templates, not HTML editing.
|
||||
- **Always set `supports.jsx` to `true`** if your block uses `<InnerBlocks />`. Without this, the InnerBlocks area will not render.
|
||||
- **Commit ACF JSON files** from the `acf/` directory to version control after creating field groups.
|
||||
- **Use semantic HTML elements** as block wrappers (`<section>`, `<article>`, `<aside>`, `<nav>`, etc.) instead of generic `<div>` elements where appropriate.
|
||||
@@ -929,7 +953,7 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
- **Do not forget to create the CSS file** referenced in `block.json`. Even if the file is empty, WordPress needs it to exist. If the file is missing, WordPress may throw an error when loading the block.
|
||||
- **Do not use `'option'` directly with `get_field()` for nested values without null checking.** Prefer `getFieldValue()` which handles missing values gracefully.
|
||||
- **Do not set `supports.multiple` to `false`** unless the block truly must be unique per page (like a homepage hero). Most blocks should allow multiple instances.
|
||||
- **Do not hardcode editor-only Tailwind classes in PHP templates** without adding them to `whitelist.php`. If a class only appears in the editor's UI (like grid column classes set via ACF fields), Tailwind will not include it in the build.
|
||||
- **Do not hardcode editor-only Tailwind classes in PHP templates** without adding them to `whitelist.php`. If a class only appears in the editor's UI (like grid column classes set via SCF/ACF fields), Tailwind will not include it in the build.
|
||||
- **Do not use the `style` attribute on the block's root element alongside `blockWrapperAttributes()` for background colors** unless the block specifically needs it. WordPress's built-in color supports (enabled via `supports.color`) handle this automatically.
|
||||
|
||||
### Debugging Tips
|
||||
@@ -938,4 +962,4 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
|
||||
- If ACF fields do not show up when editing a block, verify the field group's location rule is set to "Block is equal to [Your Block Name]".
|
||||
- If styles are not loading, confirm the `style` array in `block.json` uses the `file:./` prefix and the CSS filename matches exactly.
|
||||
- If Tailwind classes are not applying on the frontend, run `npm run build` and check that the classes are either in your templates or in `whitelist.php`.
|
||||
- If InnerBlocks are not rendering, confirm `"jsx": true` is set in the block's `supports` configuration.
|
||||
- If InnerBlocks are not rendering, confirm `"jsx": true` is set in the block's `supports` configuration.
|
||||
|
||||
+101
-116
@@ -1,6 +1,6 @@
|
||||
# Getting Started with VDI-Starter-v5
|
||||
# Getting Started with SoloFrame Evo
|
||||
|
||||
This guide walks you through setting up a local development environment for the VDI-Starter-v5 WordPress theme from scratch. It covers prerequisites, installation, configuration, and common development workflows.
|
||||
This guide walks you through setting up a local development environment for the SoloFrame Evo WordPress theme from scratch. It covers prerequisites, installation, configuration, and common development workflows.
|
||||
|
||||
---
|
||||
|
||||
@@ -15,7 +15,7 @@ This guide walks you through setting up a local development environment for the
|
||||
7. [Activating the Theme](#activating-the-theme)
|
||||
8. [Development Workflow](#development-workflow)
|
||||
9. [Project Architecture](#project-architecture)
|
||||
10. [Creating Custom ACF Blocks](#creating-custom-acf-blocks)
|
||||
10. [Creating Custom SCF/ACF Blocks](#creating-custom-scfacf-blocks)
|
||||
11. [Testing](#testing)
|
||||
12. [Code Quality](#code-quality)
|
||||
13. [Troubleshooting](#troubleshooting)
|
||||
@@ -24,7 +24,7 @@ This guide walks you through setting up a local development environment for the
|
||||
|
||||
## Overview
|
||||
|
||||
VDI-Starter-v5 is a minimal WordPress theme designed as a starting point for custom theme development. It uses a modern stack:
|
||||
SoloFrame Evo is a minimal WordPress theme designed as a starting point for custom theme development. It uses a modern stack:
|
||||
|
||||
- **Tailwind CSS v4** for utility-first styling, compiled via the Tailwind CLI
|
||||
- **ACF Pro** for custom field management and block registration
|
||||
@@ -41,14 +41,14 @@ The theme intentionally avoids heavyweight frameworks. Every PHP file in `lib/`
|
||||
|
||||
Before you begin, make sure the following tools are installed on your machine.
|
||||
|
||||
| Tool | Minimum Version | Why It Is Needed |
|
||||
|-------------|-----------------|------------------------------------------------------------------------------------------------------|
|
||||
| Node.js | 22+ | Tailwind CSS v4 CLI requires a modern Node runtime. Older versions will fail during `npm run build`. |
|
||||
| npm | Latest (bundled with Node) | Package management for JavaScript dependencies and build scripts. |
|
||||
| PHP | 8.0+ | WordPress core requirement and theme compatibility. |
|
||||
| Composer | 2.x | Installs PHP_CodeSniffer with WordPress coding standards for linting. |
|
||||
| WordPress | 6.5+ | The theme uses `wp_enqueue_script_module()`, which was introduced in WordPress 6.5. |
|
||||
| ACF Pro | Latest | All custom blocks depend on ACF Pro. Blocks will not register without it. |
|
||||
| Tool | Minimum Version | Why It Is Needed |
|
||||
| ------------- | -------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| Node.js | 22+ | Tailwind CSS v4 CLI requires a modern Node runtime. Older versions will fail during `npm run build`. |
|
||||
| npm | Latest (bundled with Node) | Package management for JavaScript dependencies and build scripts. |
|
||||
| PHP | 8.0+ | WordPress core requirement and theme compatibility. |
|
||||
| Composer | 2.x | Installs PHP_CodeSniffer with WordPress coding standards for linting. |
|
||||
| WordPress | 6.5+ | The theme uses `wp_enqueue_script_module()`, which was introduced in WordPress 6.5. |
|
||||
| ACF Pro | Latest | All custom blocks depend on ACF Pro. Blocks will not register without it. |
|
||||
|
||||
### Checking Your Versions
|
||||
|
||||
@@ -96,36 +96,36 @@ Docker gives you the most control and works on any operating system, but it requ
|
||||
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/).
|
||||
2. Use the official `wordpress` Docker image with a custom theme mount. A minimal `docker-compose.yml` might look like:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
db:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: wordpress
|
||||
MYSQL_DATABASE: wordpress
|
||||
MYSQL_USER: wordpress
|
||||
MYSQL_PASSWORD: wordpress
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
db:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: wordpress
|
||||
MYSQL_DATABASE: wordpress
|
||||
MYSQL_USER: wordpress
|
||||
MYSQL_PASSWORD: wordpress
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
wordpress:
|
||||
image: wordpress:latest
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db:3306
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_DB_NAME: wordpress
|
||||
volumes:
|
||||
- ./themes/vdi-starter-v5:/var/www/html/wp-content/themes/vdi-starter-v5
|
||||
depends_on:
|
||||
- db
|
||||
wordpress:
|
||||
image: wordpress:latest
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db:3306
|
||||
WORDPRESS_DB_USER: wordpress
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_DB_NAME: wordpress
|
||||
volumes:
|
||||
- ./themes/SoloFrame-Evo:/var/www/html/wp-content/themes/SoloFrame-Evo
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
```
|
||||
volumes:
|
||||
db_data:
|
||||
```
|
||||
|
||||
3. Run `docker compose up -d` and visit `http://localhost:8080` to complete the WordPress installation wizard.
|
||||
|
||||
@@ -136,29 +136,17 @@ volumes:
|
||||
### Step 1: Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Vincent-Design-Inc/VDI-Starter-v5.git
|
||||
cd VDI-Starter-v5
|
||||
git clone https://github.com/ksolomon/SoloFrame-Evo.git
|
||||
cd SoloFrame-Evo
|
||||
```
|
||||
|
||||
If you are contributing to an existing project, clone it into your local WordPress `wp-content/themes/` directory so WordPress can detect it:
|
||||
|
||||
```bash
|
||||
cd /path/to/your/local-wp-site/wp-content/themes/
|
||||
git clone https://github.com/Vincent-Design-Inc/VDI-Starter-v5.git
|
||||
git clone https://github.com/ksolomon/SoloFrame-Evo.git
|
||||
```
|
||||
|
||||
If you cloned it elsewhere, you can symlink it into the themes directory:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
ln -s /path/to/VDI-Starter-v5 /path/to/wp-content/themes/vdi-starter-v5
|
||||
|
||||
# Windows (run in an elevated Command Prompt)
|
||||
mklink /D "C:\path\to\wp-content\themes\vdi-starter-v5" "C:\path\to\VDI-Starter-v5"
|
||||
```
|
||||
|
||||
Using a symlink means your local edits are immediately reflected in WordPress without copying files.
|
||||
|
||||
### Step 2: Install PHP Dependencies
|
||||
|
||||
```bash
|
||||
@@ -173,9 +161,9 @@ This installs PHP_CodeSniffer and the WordPress Coding Standards (WPCS) ruleset.
|
||||
npm install
|
||||
```
|
||||
|
||||
This installs the frontend build toolchain: Tailwind CSS v4 and its CLI, BrowserSync, Playwright, dotenv, and other utilities. Tailwind v4 uses the `@tailwindcss/cli` package directly (no `tailwind.config.js` file is needed -- configuration lives in `styles/theme.css`).
|
||||
This installs the frontend build toolchain: Tailwind CSS v4 and its CLI, BrowserSync, Playwright, dotenv, and other utilities. Tailwind v4 uses the `@tailwindcss/cli` package directly. No `tailwind.config.js` file is needed -- configuration lives in `styles/theme.css`.
|
||||
|
||||
### Step 4: Configure Environment Variables
|
||||
## Environment Configuration
|
||||
|
||||
Copy the example environment file and edit it:
|
||||
|
||||
@@ -185,16 +173,16 @@ cp .env.example .env
|
||||
|
||||
Open `.env` and set the two variables:
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
|-------------------|-----------------------------------------------------------------------------------------------|------------------------------|
|
||||
| `LOCALHOST_URL` | The full URL of your local WordPress site, including the scheme (`http` or `https`) | `https://vdi-starter.local` |
|
||||
| `BROWSERSYNC_PORT`| The port BrowserSync should listen on. Defaults to `5000` if not set. | `5000` |
|
||||
| Variable | Description | Example Value |
|
||||
| ------------------- | ----------------------------------------------------------------------------------- | ------------------------------ |
|
||||
| `LOCALHOST_URL` | The full URL of your local WordPress site, including the scheme (`http` or `https`) | `https://soloframe-evo.local` |
|
||||
| `BROWSERSYNC_PORT` | The port BrowserSync should listen on. Defaults to `5000` if not set. | `5000` |
|
||||
|
||||
The `LOCALHOST_URL` must match exactly what your local WordPress environment responds to. If you are using Local by Flywheel with SSL enabled, include `https://`. If you are using Docker on port 8080, use `http://localhost:8080`.
|
||||
|
||||
BrowserSync proxies this URL and injects a live-reload script, so any change you make to PHP templates, CSS, or JS files will automatically refresh the browser.
|
||||
|
||||
### Step 5: Build Assets for the First Time
|
||||
## Building Assets
|
||||
|
||||
Before activating the theme, compile the Tailwind CSS so the theme has its stylesheet:
|
||||
|
||||
@@ -204,7 +192,7 @@ npm run build
|
||||
|
||||
This runs:
|
||||
|
||||
```
|
||||
```bash
|
||||
npx @tailwindcss/cli -i ./styles/theme.css -o ./static/dist/theme.css --optimize
|
||||
```
|
||||
|
||||
@@ -227,15 +215,11 @@ Log into your local WordPress admin dashboard and navigate to **Appearance > The
|
||||
> - **Creates 4 default pages:** Home, News, Page Not Found (Error 404), and Contact Us.
|
||||
> - **Sets WordPress to use a static front page:** Home becomes the front page, News becomes the posts page. This overrides any existing "Reading Settings".
|
||||
> - **Deletes the default "Hello World" post** (ID 1) and the **sample page** (ID 2). These are trashed permanently (`wp_delete_post` with `$force_delete = true`).
|
||||
> - **Installs 7 plugins** from external URLs and selectively activates them:
|
||||
> 1. **ACF Pro** -- installed and activated (from `https://docs.vincentdevelopment.ca/files/advanced-custom-fields-pro.zip`)
|
||||
> 2. **Gravity Forms** -- installed and activated (from `https://docs.vincentdevelopment.ca/files/gravity-forms.zip`)
|
||||
> 3. **UpdraftPlus** -- installed, NOT activated (from WordPress.org)
|
||||
> 4. **Simple History** -- installed and activated (from WordPress.org)
|
||||
> 5. **The SEO Framework** -- installed and activated (from WordPress.org)
|
||||
> 6. **Better Search Replace** -- installed and activated (from WordPress.org)
|
||||
> 7. **Google Site Kit** -- installed, NOT activated (from WordPress.org)
|
||||
> - **Creates an "Owner" role** (administrator capabilities minus plugin/theme management).
|
||||
> - **Installs 4 plugins** from external URLs and selectively activates them:
|
||||
> 1. **Secure Custom Fields** -- installed and activated (from WordPress.org)
|
||||
> 2. **Simple History** -- installed and activated (from WordPress.org)
|
||||
> 3. **The SEO Framework** -- installed and activated (from WordPress.org)
|
||||
> 4. **Better Search Replace** -- installed and activated (from WordPress.org)
|
||||
> - **Writes an installation log** to `wp-content/mu-plugin-install.log`.
|
||||
>
|
||||
> **Do NOT activate this theme on an existing production site without reviewing `lib/activation.php` first.** The activation routine is designed for fresh installs and will modify pages, settings, and plugin state without confirmation.
|
||||
@@ -271,7 +255,7 @@ This runs `bin/.watch.js`, which starts BrowserSync and watches for file changes
|
||||
|
||||
When you edit a `.js` file in `static/js/`, BrowserSync injects the updated script without a full page reload (hot injection).
|
||||
|
||||
The dev server is accessible at the `LOCALHOST_URL` you configured, proxied through the `BROWSERSYNC_PORT`. For example, if your `.env` has `LOCALHOST_URL=https://vdi-starter.local` and `BROWSERSYNC_PORT=5000`, your dev URL is `https://vdi-starter.local` with BrowserSync overlay on port 5000.
|
||||
The dev server is accessible at the `LOCALHOST_URL` you configured, proxied through the `BROWSERSYNC_PORT`. For example, if your `.env` has `LOCALHOST_URL=http://soloframe-evo.local` and `BROWSERSYNC_PORT=5000`, your dev URL is `http://soloframe-evo.local` with BrowserSync overlay on port 5000.
|
||||
|
||||
### Building for Production
|
||||
|
||||
@@ -291,27 +275,27 @@ This compiles Tailwind CSS with `--optimize` enabled, which minifies the output
|
||||
|
||||
Understanding the directory layout helps you know where to find things and where to put new files.
|
||||
|
||||
```
|
||||
VDI-Starter-v5/
|
||||
├── acf/ # ACF Pro field group JSON (auto-synced)
|
||||
```plain
|
||||
SoloFrame-Evo/
|
||||
├── acf/ # SCF/ACF Pro field group JSON (auto-synced)
|
||||
│ └── group_*.json # One file per field group
|
||||
├── bin/
|
||||
│ ├── .watch.js # BrowserSync dev server script
|
||||
│ └── .utils.js # Shared build utilities (Tailwind compilation)
|
||||
│ └── .utils.js # Shared build utilities (Tailwind compilation)
|
||||
├── content/
|
||||
│ └── basic-wp-test-content.xml # Sample content for testing
|
||||
├── docs/ # Documentation (this guide lives here)
|
||||
├── lib/ # PHP utility classes (auto-loaded)
|
||||
│ ├── activation.php # Theme activation routine (pages, plugins, settings)
|
||||
│ ├── class-acf.php # ACF JSON load/save path configuration
|
||||
│ ├── class-acf.php # SCF/ACF JSON load/save path configuration
|
||||
│ ├── class-breadcrumbs.php # Breadcrumb navigation helper
|
||||
│ ├── class-enqueue.php # Frontend/backend/editor asset enqueueing
|
||||
│ ├── class-menuitems.php # Custom menu item handling
|
||||
│ ├── class-menuitems.php # Custom menu item handling
|
||||
│ ├── class-resources.php # Resource management
|
||||
│ ├── extras.php # Miscellaneous helper functions
|
||||
│ ├── extras.php # Miscellaneous helper functions
|
||||
│ ├── helpers.php # Template helper functions
|
||||
│ ├── hooks.php # Theme hooks (menus, sidebars, cleanup, SVG uploads)
|
||||
│ ├── search-features.php # Enhanced search functionality
|
||||
│ ├── search-features.php # Enhanced search functionality
|
||||
│ └── show-template.php # Template debugging (shows which template is loaded)
|
||||
├── static/
|
||||
│ ├── dist/
|
||||
@@ -350,7 +334,7 @@ VDI-Starter-v5/
|
||||
│ ├── page-children/
|
||||
│ └── section/
|
||||
├── tests/
|
||||
│ └── site-a11y.spec.js # Playwright accessibility tests
|
||||
│ └── site-a11y.spec.js # Playwright accessibility tests
|
||||
├── .env.example # Environment variable template
|
||||
├── composer.json # PHP dependencies (PHPCS + WPCS)
|
||||
├── functions.php # Theme bootstrap (loads all lib/*.php files, registers ACF blocks)
|
||||
@@ -373,7 +357,7 @@ foreach ( glob( __DIR__ . '/lib/*.php' ) as $filename ) {
|
||||
|
||||
This means adding a new file to `lib/` automatically includes it. No manual `require` statements are needed. However, be aware that files are loaded in alphabetical order. If one file depends on something defined in another, you may need to rename files with numeric prefixes to control load order.
|
||||
|
||||
### How ACF Blocks Are Registered
|
||||
### How SCF/ACF Blocks Are Registered
|
||||
|
||||
The `regACFBlocks()` function in `functions.php` scans the `views/blocks/` directory at runtime:
|
||||
|
||||
@@ -398,21 +382,21 @@ Each subdirectory that contains a `block.json` file is registered as a Gutenberg
|
||||
The Tailwind entry point is `styles/theme.css`. It imports sub-stylesheets using CSS `@import` directives:
|
||||
|
||||
```css
|
||||
@import "tailwindcss"; /* Tailwind v4 framework */
|
||||
@import "./base/index.css"; /* Base styles */
|
||||
@import "./navigation/index.css";/* Navigation styles */
|
||||
@import "./fonts/lineicons.css"; /* Icon font */
|
||||
@import "./base/break-out.css"; /* Break-out utility */
|
||||
@import "./components/index.css";/* Component styles */
|
||||
@import "./blocks/index.css"; /* Block-specific styles */
|
||||
@plugin "@tailwindcss/typography"; /* Typography plugin */
|
||||
@import "tailwindcss"; /* Tailwind v4 framework */
|
||||
@import "./base/index.css"; /* Base styles */
|
||||
@import "./navigation/index.css"; /* Navigation styles */
|
||||
@import "./fonts/lineicons.css"; /* Icon font */
|
||||
@import "./base/break-out.css"; /* Break-out utility */
|
||||
@import "./components/index.css"; /* Component styles */
|
||||
@import "./blocks/index.css"; /* Block-specific styles */
|
||||
@plugin "@tailwindcss/typography"; /* Tailwind Typography plugin */
|
||||
```
|
||||
|
||||
When you create a new block or component, add its styles to the appropriate subdirectory and make sure it is imported through the corresponding `index.css` file.
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom ACF Blocks
|
||||
## Creating Custom SCF/ACF Blocks
|
||||
|
||||
To create a new ACF block, use the `boilerplate` directory as a starting point:
|
||||
|
||||
@@ -424,7 +408,7 @@ To create a new ACF block, use the `boilerplate` directory as a starting point:
|
||||
|
||||
2. Edit `views/blocks/my-block/block.json`:
|
||||
- Change `"name"` to `"acf/my-block"`
|
||||
- Change `"title"` to a human-readable name like `"My Block (VDI)"`
|
||||
- Change `"title"` to a human-readable name like `"My Block"`
|
||||
- Update `"description"`, `"icon"`, and `"keywords"` as appropriate
|
||||
- If this block should only be nested inside another block, add a `"parent"` array (see `button/block.json` for an example)
|
||||
|
||||
@@ -432,7 +416,7 @@ To create a new ACF block, use the `boilerplate` directory as a starting point:
|
||||
|
||||
4. If the block needs ACF field groups, create them in the WordPress admin under **Custom Fields > Field Groups** and associate them with the block. ACF will save the field group JSON to the `acf/` directory, which you should commit to version control.
|
||||
|
||||
5. Add block-specific styles in `styles/blocks/` and import them through `styles/blocks/index.css`.
|
||||
5. Add block-specific styles in `my-block.css`.
|
||||
|
||||
The block will be automatically discovered and registered on the next page load because `regACFBlocks()` scans the directory on every `init` hook.
|
||||
|
||||
@@ -502,12 +486,13 @@ The compilation pipeline is: `styles/theme.css` (entry point with `@import` dire
|
||||
|
||||
**Symptom:** The block inserter in the Gutenberg editor does not show custom blocks like "Homepage Hero" or "Section".
|
||||
|
||||
**Solution:** ACF Pro must be installed and activated. Blocks are registered by scanning `views/blocks/*/block.json` on the `init` hook. Without ACF Pro, `register_block_type()` still runs, but ACF blocks require the ACF plugin to provide field data and rendering.
|
||||
**Solution:** SCF (installed by default on theme activation) or ACF Pro must be installed and activated. Blocks are registered by scanning `views/blocks/*/block.json` on the `init` hook. Without SCF/ACF Pro, `register_block_type()` still runs, but blocks require the SCF/ACF plugin to provide field data and rendering.
|
||||
|
||||
Also check that:
|
||||
|
||||
- The block directory contains a valid `block.json` file.
|
||||
- The block directory is not named `boilerplate` (this is excluded by design).
|
||||
- ACF Pro is activated (not just installed).
|
||||
- SCF/ACF Pro is activated (not just installed).
|
||||
|
||||
### Menus Are Not Rendering
|
||||
|
||||
@@ -566,24 +551,24 @@ Check `wp-content/mu-plugin-install.log` for detailed error messages. Each plugi
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Command | Purpose |
|
||||
|---------------------|--------------------------------------------------------------------|
|
||||
| `npm run start` | Start BrowserSync dev server with live reload (alias for `watch`) |
|
||||
| `npm run watch` | Start BrowserSync dev server with live reload |
|
||||
| `npm run build` | Compile Tailwind CSS for production (minified, optimized) |
|
||||
| `composer lint` | Run PHP_CodeSniffer against WordPress coding standards |
|
||||
| `composer fix` | Auto-fix PHP_CodeSniffer violations |
|
||||
| `npx playwright test` | Run Playwright end-to-end tests |
|
||||
| Command | Purpose |
|
||||
| --------------------- | -------------------------------------------------------------------- |
|
||||
| `npm run start` | Start BrowserSync dev server with live reload (alias for `watch`) |
|
||||
| `npm run watch` | Start BrowserSync dev server with live reload |
|
||||
| `npm run build` | Compile Tailwind CSS for production (minified, optimized) |
|
||||
| `composer lint` | Run PHP_CodeSniffer against WordPress coding standards |
|
||||
| `composer fix` | Auto-fix PHP_CodeSniffer violations |
|
||||
| `npx playwright test` | Run Playwright end-to-end tests |
|
||||
|
||||
| File | Purpose |
|
||||
|-----------------------------------|------------------------------------------------------|
|
||||
| `.env` | Local environment config (`LOCALHOST_URL`, `BROWSERSYNC_PORT`) |
|
||||
| `styles/theme.css` | Tailwind CSS entry point (edit this to add imports) |
|
||||
| `static/dist/theme.css` | Compiled CSS output (generated, do not edit manually)|
|
||||
| `views/blocks/*/block.json` | ACF block registration files |
|
||||
| `lib/activation.php` | Theme activation routine (creates pages, installs plugins) |
|
||||
| `lib/class-enqueue.php` | Frontend/backend/editor asset loading |
|
||||
| `lib/hooks.php` | Menu registration, sidebars, theme support, cleanup |
|
||||
| `functions.php` | Theme bootstrap (auto-loads all `lib/*.php` files) |
|
||||
| `theme.json` | WordPress theme configuration (colors, typography, layout) |
|
||||
| `acf/group_*.json` | ACF field group definitions (version-controlled) |
|
||||
| File | Purpose |
|
||||
| ----------------------------------- | -------------------------------------------------------------- |
|
||||
| `.env` | Local environment config (`LOCALHOST_URL`, `BROWSERSYNC_PORT`) |
|
||||
| `styles/theme.css` | Tailwind CSS entry point (edit this to add imports) |
|
||||
| `static/dist/theme.css` | Compiled CSS output (generated, do not edit manually) |
|
||||
| `views/blocks/*/block.json` | ACF block registration files |
|
||||
| `lib/activation.php` | Theme activation routine (creates pages, installs plugins) |
|
||||
| `lib/class-enqueue.php` | Frontend/backend/editor asset loading |
|
||||
| `lib/hooks.php` | Menu registration, sidebars, theme support, cleanup |
|
||||
| `functions.php` | Theme bootstrap (auto-loads all `lib/*.php` files) |
|
||||
| `theme.json` | WordPress theme configuration (colors, typography, layout) |
|
||||
| `acf/group_*.json` | ACF field group definitions (version-controlled) |
|
||||
|
||||
+73
-61
@@ -1,46 +1,56 @@
|
||||
# VDI-Starter-v5 Theme Reference
|
||||
# SoloFrame Evo Theme Reference
|
||||
|
||||
> Quick-lookup reference for the VDI-Starter-v5 WordPress theme. Covers hooks, filters, design tokens, CSS architecture, JS modules, helper functions, class APIs, CLI commands, deployment, and testing.
|
||||
> Quick-lookup reference for the SoloFrame Evo WordPress theme. Covers hooks, filters, design tokens, CSS architecture, JS modules, helper functions, class APIs, CLI commands, deployment, and testing.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Hooks and Filters](#hooks-and-filters)
|
||||
- [hooks.php (BasicWP Namespace)](#hooksphp-basicwp-namespace)
|
||||
- [extras.php](#extrasphp)
|
||||
- [helpers.php](#helpersphp)
|
||||
- [class-enqueue.php](#class-enqueuephp)
|
||||
- [class-breadcrumbs.php](#class-breadcrumbsphp)
|
||||
- [class-resources.php](#class-resourcesphp)
|
||||
- [theme.json Design Tokens](#themejson-design-tokens)
|
||||
- [Colors](#colors)
|
||||
- [Font Sizes](#font-sizes)
|
||||
- [Font Family](#font-family)
|
||||
- [Layout](#layout)
|
||||
- [Spacing Units](#spacing-units)
|
||||
- [CSS Architecture](#css-architecture)
|
||||
- [Import Order](#import-order)
|
||||
- [Adding a New Stylesheet](#adding-a-new-stylesheet)
|
||||
- [JS Module Dependency Graph](#js-module-dependency-graph)
|
||||
- [Script Module IDs](#script-module-ids)
|
||||
- [Navigation Class API](#navigation-class-api)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [Class Reference](#class-reference)
|
||||
- [CLI Commands](#cli-commands)
|
||||
- [Deployment (GitHub Actions)](#deployment-github-actions)
|
||||
- [Testing](#testing)
|
||||
- [SoloFrame Evo Theme Reference](#soloframe-evo-theme-reference)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Hooks and Filters](#hooks-and-filters)
|
||||
- [hooks.php (SoloFrameEvo Namespace)](#hooksphp-soloframeevo-namespace)
|
||||
- [extras.php](#extrasphp)
|
||||
- [helpers.php](#helpersphp)
|
||||
- [class-enqueue.php](#class-enqueuephp)
|
||||
- [class-breadcrumbs.php](#class-breadcrumbsphp)
|
||||
- [class-resources.php](#class-resourcesphp)
|
||||
- [theme.json Design Tokens](#themejson-design-tokens)
|
||||
- [Colors](#colors)
|
||||
- [Font Sizes](#font-sizes)
|
||||
- [Font Family](#font-family)
|
||||
- [Layout](#layout)
|
||||
- [Spacing Units](#spacing-units)
|
||||
- [CSS Architecture](#css-architecture)
|
||||
- [Import Order](#import-order)
|
||||
- [Adding a New Stylesheet](#adding-a-new-stylesheet)
|
||||
- [JS Module Dependency Graph](#js-module-dependency-graph)
|
||||
- [Script Module IDs](#script-module-ids)
|
||||
- [Navigation Class API](#navigation-class-api)
|
||||
- [Constructor](#constructor)
|
||||
- [Methods](#methods)
|
||||
- [Helper Functions](#helper-functions)
|
||||
- [Class Reference](#class-reference)
|
||||
- [Breadcrumbs Method Details](#breadcrumbs-method-details)
|
||||
- [CLI Commands](#cli-commands)
|
||||
- [Deployment (GitHub Actions)](#deployment-github-actions)
|
||||
- [Deployment Steps](#deployment-steps)
|
||||
- [rsync Flags](#rsync-flags)
|
||||
- [Testing](#testing)
|
||||
- [Accessibility Tests](#accessibility-tests)
|
||||
- [PHP Linting](#php-linting)
|
||||
- [Playwright Configuration](#playwright-configuration)
|
||||
|
||||
---
|
||||
|
||||
## Hooks and Filters
|
||||
|
||||
### hooks.php (BasicWP Namespace)
|
||||
### hooks.php (SoloFrameEvo Namespace)
|
||||
|
||||
All hooks in this file live under the `BasicWP` namespace.
|
||||
All hooks in this file live under the `SoloFrameEvo` namespace.
|
||||
|
||||
| Hook | Type | Priority | Args | Description |
|
||||
|------|------|----------|------|-------------|
|
||||
| ------ | ------ | ---------- | ------ | ------------- |
|
||||
| `wp_head` | Action | 0 | — | Adds Google Fonts `<link rel="preconnect">` tags |
|
||||
| `register_nav_menus()` | Direct call | — | — | Registers three menus: `main_navigation`, `aux_navigation`, `footer_navigation` |
|
||||
| `widgets_init` | Action | — | — | Registers four sidebars: `sidebar-primary`, `sidebar-page`, `footer-1`, `footer-2`, `footer-3` |
|
||||
@@ -57,6 +67,7 @@ All hooks in this file live under the `BasicWP` namespace.
|
||||
**The `init` (priority 1) hook performs aggressive cleanup:**
|
||||
|
||||
Removes:
|
||||
|
||||
- Emoji detection and styles (`remove_action` on `wp_head`)
|
||||
- Block library styles (`wp-block-library`)
|
||||
- Global styles (`global-styles`)
|
||||
@@ -67,6 +78,7 @@ Removes:
|
||||
- WLW manifest link (`wlwmanifest_link`)
|
||||
|
||||
Adds theme supports:
|
||||
|
||||
- `post-thumbnails`
|
||||
- `title-tag`
|
||||
- `html5` (search-form, comment-form, comment-list, gallery, caption, style, script)
|
||||
@@ -80,7 +92,7 @@ Adds theme supports:
|
||||
### extras.php
|
||||
|
||||
| Hook / Filter | Type | Description |
|
||||
|---------------|------|-------------|
|
||||
| --------------- | ------ | ------------- |
|
||||
| `hasSidebar` | Filter | Controls sidebar display based on an ACF true/false field on the current page |
|
||||
| `body_class` | Filter | Appends `has-sidebar` class to `<body>` when a sidebar is present |
|
||||
| `the_content` | Filter | `divWrapper()` wraps `<iframe>` and embed elements in `<div class="embed">` |
|
||||
@@ -94,10 +106,10 @@ Adds theme supports:
|
||||
### helpers.php
|
||||
|
||||
| Hook / Filter | Type | Priority | Args | Description |
|
||||
|---------------|------|----------|------|-------------|
|
||||
| --------------- | ------ | ---------- | ------ | ------------- |
|
||||
| `custom_menu_order` | Filter | 10 | 1 | Enables custom admin menu ordering |
|
||||
| `menu_order` | Filter | 10 | 1 | Defines the custom admin menu order |
|
||||
| `block_categories_all` | Filter | 10 | — | Adds the `vdi-blocks` category to the block editor |
|
||||
| `block_categories_all` | Filter | 10 | — | Adds the `sf-blocks` category to the block editor |
|
||||
| `init` | Action | — | — | Registers the ACF "Global Fields" options page |
|
||||
|
||||
---
|
||||
@@ -105,7 +117,7 @@ Adds theme supports:
|
||||
### class-enqueue.php
|
||||
|
||||
| Hook | Type | Method | Description |
|
||||
|------|------|--------|-------------|
|
||||
| ------ | ------ | -------- | ------------- |
|
||||
| `wp_enqueue_scripts` | Action | `enqFEAssets()` | Loads frontend CSS and JS |
|
||||
| `admin_enqueue_scripts` | Action | `enqBEAssets()` | Loads admin CSS and JS |
|
||||
| `enqueue_block_editor_assets` | Action | `enqEditorAssets()` | Loads block editor CSS |
|
||||
@@ -117,7 +129,7 @@ Adds theme supports:
|
||||
The `Breadcrumbs` class generates Schema.org-compatible breadcrumb markup. Context-specific methods:
|
||||
|
||||
| Method | Context |
|
||||
|--------|---------|
|
||||
| -------- | --------- |
|
||||
| `getHomeBreadcrumb()` | Site front page |
|
||||
| `getBlogPostsIndexBreadcrumb()` | Blog posts index |
|
||||
| `getSinglePostBreadcrumbs()` | Single post |
|
||||
@@ -134,7 +146,7 @@ The `Breadcrumbs` class generates Schema.org-compatible breadcrumb markup. Conte
|
||||
### class-resources.php
|
||||
|
||||
| Hook | Type | Description |
|
||||
|------|------|-------------|
|
||||
| ------ | ------ | ------------- |
|
||||
| `init` | Action | Registers the `resources` custom post type |
|
||||
| `post_type_link` | Filter | Customizes resource permalinks to `/resources/{term-slug}/{post-name}` |
|
||||
|
||||
@@ -149,7 +161,7 @@ The `resources` CPT uses a URL rewrite pattern that incorporates the first taxon
|
||||
All theme colors map to CSS custom properties. Use the CSS variable in your stylesheets or reference the slug in the block editor.
|
||||
|
||||
| Slug | CSS Variable | Name | Value |
|
||||
|------|-------------|------|-------|
|
||||
| ------ | ------------- | ------ | ------- |
|
||||
| `black` | — | Black | `#000` |
|
||||
| `white` | — | White | `#fff` |
|
||||
| `theme-bg` | `var(--color-background)` | Theme Background | Dynamic |
|
||||
@@ -187,7 +199,7 @@ The dynamic colors (`theme-*`) resolve to CSS custom properties that can be over
|
||||
### Font Sizes
|
||||
|
||||
| Slug | CSS Variable | Name |
|
||||
|------|-------------|------|
|
||||
| ------ | ------------- | ------ |
|
||||
| `base` | `var(--text-base)` | Base |
|
||||
| `text-14px` | `var(--text-14px)` | Text 14px |
|
||||
| `text-16px` | `var(--text-16px)` | Text 16px |
|
||||
@@ -223,7 +235,7 @@ The dynamic colors (`theme-*`) resolve to CSS custom properties that can be over
|
||||
### Font Family
|
||||
|
||||
| Slug | CSS Variable | Name |
|
||||
|------|-------------|------|
|
||||
| ------ | ------------- | ------ |
|
||||
| `theme-sans` | `var(--font-sans)` | Theme Sans |
|
||||
|
||||
```css
|
||||
@@ -237,7 +249,7 @@ body {
|
||||
### Layout
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| --------- | ------- |
|
||||
| `contentSize` | `100%` |
|
||||
| `wideSize` | `1536px` |
|
||||
|
||||
@@ -259,7 +271,7 @@ The entry point is `styles/theme.css`. All imports use the CSS `@import` syntax
|
||||
|
||||
### Import Order
|
||||
|
||||
```
|
||||
```plain
|
||||
styles/theme.css
|
||||
|
|
||||
+-- @import "tailwindcss" # Tailwind CSS v4 base
|
||||
@@ -325,12 +337,12 @@ Example -- adding a new `cards.css` component:
|
||||
|
||||
## JS Module Dependency Graph
|
||||
|
||||
```
|
||||
```plain
|
||||
theme.js (entry point)
|
||||
├── Navigation.js # Mobile menu, sliding viewport, keyboard nav
|
||||
├── Navigation.js # Mobile menu, sliding viewport, keyboard nav
|
||||
├── backToTop.js # BackToTopButton custom element
|
||||
├── button.js # ButtonComponent custom element, registerButtonComponent
|
||||
├── GetHeaderHeight.js # Sets --header-height CSS variable
|
||||
├── GetHeaderHeight.js # Sets --header-height CSS variable
|
||||
└── TagExternalLinks.js # Adds target="_blank" rel="noopener" to external links
|
||||
|
||||
admin.js
|
||||
@@ -342,16 +354,16 @@ admin.js
|
||||
WordPress registers these script modules via `wp_register_script_module()`:
|
||||
|
||||
| Module ID | Source | Dependencies |
|
||||
|-----------|--------|--------------|
|
||||
| `basicwp-theme` | `theme.js` | None |
|
||||
| `basicwp-button` | `button.js` | `basicwp-theme` |
|
||||
| `basicwp-admin` | `admin.js` | `basicwp-button` |
|
||||
| ----------- | -------- | -------------- |
|
||||
| `sf-evo-theme` | `theme.js` | None |
|
||||
| `sf-evo-button` | `button.js` | `sf-evo-theme` |
|
||||
| `sf-evo-admin` | `admin.js` | `sf-evo-button` |
|
||||
|
||||
**Loading in a template:**
|
||||
|
||||
```php
|
||||
wp_enqueue_script_module('basicwp-theme');
|
||||
wp_enqueue_script_module('basicwp-button');
|
||||
wp_enqueue_script_module('sf-evo-theme');
|
||||
wp_enqueue_script_module('sf-evo-button');
|
||||
```
|
||||
|
||||
---
|
||||
@@ -371,7 +383,7 @@ const nav = new Navigation(toggleId, menuSelector);
|
||||
### Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| -------- | ------------- |
|
||||
| `desktopMenuDropdowns()` | Enables dropdown menus for desktop navigation |
|
||||
| `mobileMenuToggle()` | Toggles the mobile hamburger menu open/closed |
|
||||
| `initializeSlidingViewport()` | Sets up the sliding mobile menu structure |
|
||||
@@ -400,11 +412,11 @@ nav.initializeSlidingViewport();
|
||||
## Helper Functions
|
||||
|
||||
| Function | File | Signature | Description |
|
||||
|----------|------|-----------|-------------|
|
||||
| ---------- | ------ | ----------- | ------------- |
|
||||
| `getFieldValue` | helpers.php | `getFieldValue($field_path)` | Retrieves nested ACF values using dot notation. E.g., `getFieldValue('contact_info.phone')` resolves `get_field('contact_info', 'option')['phone']`. Uses `'option'` for Global Fields. |
|
||||
| `blockWrapperAttributes` | helpers.php | `blockWrapperAttributes($classes, $is_preview)` | Returns block wrapper attributes. In preview mode returns `class="..."`; on frontend returns `get_block_wrapper_attributes()`. |
|
||||
| `customMenuOrder` | helpers.php | `customMenuOrder($menu_ord)` | Customizes WordPress admin menu order. |
|
||||
| `blockCategories` | helpers.php | `blockCategories($categories)` | Adds the `vdi-blocks` category to the block editor. |
|
||||
| `blockCategories` | helpers.php | `blockCategories($categories)` | Adds the `sf-blocks` category to the block editor. |
|
||||
| `consoleLog` | helpers.php | `consoleLog($data)` | Outputs data to the browser console via `<script>console.log()</script>`. |
|
||||
| `customExcerpt` | helpers.php | `customExcerpt($text, $number_of_words, $more)` | Generates custom excerpts that end at sentence boundaries instead of mid-sentence. |
|
||||
| `escEmbeds` | helpers.php | `escEmbeds()` | Returns an allowed HTML array for iframe/embed content (used with `wp_kses`). |
|
||||
@@ -435,7 +447,7 @@ $excerpt = customExcerpt(get_the_content(), 30, '...');
|
||||
## Class Reference
|
||||
|
||||
| Class | File | Key Methods | Description |
|
||||
|-------|------|-------------|-------------|
|
||||
| ------- | ------ | ------------- | ------------- |
|
||||
| `Enqueue` | `class-enqueue.php` | `enqFEAssets()`, `enqBEAssets()`, `enqEditorAssets()` | Manages all asset loading: frontend, admin, and editor |
|
||||
| `ACF` | `class-acf.php` | `saveJson($path)`, `loadJson($paths)` | Sets ACF JSON save/load paths for field group synchronization |
|
||||
| `Breadcrumbs` | `class-breadcrumbs.php` | `generate()`, `render()`, plus per-context methods (see below) | Generates Schema.org-compatible breadcrumb markup |
|
||||
@@ -446,7 +458,7 @@ $excerpt = customExcerpt(get_the_content(), 30, '...');
|
||||
### Breadcrumbs Method Details
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| -------- | --------- | ------------- |
|
||||
| `generate()` | `array` | Builds breadcrumb data array for the current context |
|
||||
| `render()` | `string` | Outputs breadcrumb HTML with Schema.org markup |
|
||||
| `getHomeBreadcrumb()` | `array` | Breadcrumb for the front page |
|
||||
@@ -472,7 +484,7 @@ echo $breadcrumbs->render();
|
||||
## CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| --------- | ------------- |
|
||||
| `npm run build` | Compiles Tailwind CSS v4 from `styles/theme.css` to `static/dist/theme.css` with `--optimize` |
|
||||
| `npm run start` | Starts BrowserSync dev server with live reloading (alias for `npm run watch`) |
|
||||
| `npm run watch` | Runs `.watch.js` -- BrowserSync with CSS injection on changes |
|
||||
@@ -488,11 +500,11 @@ echo $breadcrumbs->render();
|
||||
The deployment workflow is defined in `.github/workflows/wpengine.yml`.
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| --------- | ------- |
|
||||
| Trigger | `workflow_dispatch` (manual). Push to `main` trigger is commented out. |
|
||||
| Skip condition | Commits containing `#skipGA` in the message are skipped |
|
||||
| Target path | `wp-content/themes/vdi-v5` |
|
||||
| WP Engine environment | `vdiv5` |
|
||||
| Target path | `wp-content/themes/ soloframe-evo` |
|
||||
| WP Engine environment | `soloframe-evo` |
|
||||
| SSH key secret | `WPE_SSHG_KEY_PRIVATE` |
|
||||
|
||||
### Deployment Steps
|
||||
@@ -506,12 +518,12 @@ The deployment workflow is defined in `.github/workflows/wpengine.yml`.
|
||||
|
||||
### rsync Flags
|
||||
|
||||
```
|
||||
```plain
|
||||
-azvr --inplace --delete --exclude=".*"
|
||||
```
|
||||
|
||||
| Flag | Meaning |
|
||||
|------|---------|
|
||||
| ------ | --------- |
|
||||
| `-a` | Archive mode (preserve permissions, timestamps, etc.) |
|
||||
| `-z` | Compress during transfer |
|
||||
| `-v` | Verbose output |
|
||||
@@ -554,4 +566,4 @@ Auto-fixes PHPCS violations where possible.
|
||||
|
||||
### Playwright Configuration
|
||||
|
||||
The Playwright config (`playwright.config.js`) is currently set to run on **Chromium only**. Firefox and WebKit browsers are commented out but available for enabling.
|
||||
The Playwright config (`playwright.config.js`) is currently set to run on **Chromium only**. Firefox and WebKit browsers are commented out but available for enabling.
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
# Onboarding Documentation Design
|
||||
|
||||
## Goal
|
||||
|
||||
Write thorough documentation that enables a new developer (team member or external contributor) to set up, understand, and work with the VDI-Starter-v5 WordPress theme with minimal hand-holding.
|
||||
|
||||
## Audience
|
||||
|
||||
Both internal team members and external contributors, served with layered docs — quick-start up top, deep dives below.
|
||||
|
||||
## Approach
|
||||
|
||||
Keep the existing README.md as a project overview (trimmed of deep-dive content that moves to `docs/`). Add a `docs/` directory with four focused guides. The README links to each guide.
|
||||
|
||||
## Doc Structure
|
||||
|
||||
```
|
||||
docs/
|
||||
├── getting-started.md # Setup from zero, local WP, env config, first build
|
||||
├── architecture.md # Bootstrap flow, layers, conventions, namespacing, globals
|
||||
├── creating-blocks.md # Step-by-step ACF block creation with block.json, fields, CSS
|
||||
└── reference.md # Hooks/filters, theme.json tokens, CSS architecture, JS modules, deployment
|
||||
```
|
||||
|
||||
Plus updates to README.md: trim deep-dive content, add Documentation section with links, fix known discrepancies.
|
||||
|
||||
## Writing Style
|
||||
|
||||
Detailed and explanatory — include context and rationale ("why"), not just "what" and "how". Short paragraphs with code examples and tables where appropriate. Designed to be read end-to-end by a newcomer, but also scannable for quick lookup.
|
||||
|
||||
## Guide Contents
|
||||
|
||||
### 1. getting-started.md
|
||||
|
||||
**Purpose:** Get a developer from zero to a running local environment.
|
||||
|
||||
- Prerequisites (Node 22+, pnpm/npm, PHP 8+, Composer, Local by Flywheel or equivalent)
|
||||
- Local WordPress setup options (Local by Flywheel, DevKinsta, Docker) with pros/cons
|
||||
- Clone, install, build, run steps (npm install, composer install, npm run build, npm run start/watch)
|
||||
- .env configuration (LOCALHOST_URL, BROWSERSYNC_PORT)
|
||||
- BrowserSync behavior (what triggers reload, which port, how to configure)
|
||||
- What happens on theme activation — **important warning**:
|
||||
- Plugin auto-installation (7 plugins downloaded from URLs)
|
||||
- Default page creation (Home, News, 404, Contact Us)
|
||||
- WordPress reading settings configuration
|
||||
- Owner role creation
|
||||
- Warning: do not activate on existing sites without reviewing `lib/activation.php`
|
||||
- Troubleshooting common issues:
|
||||
- CSS not compiling → run `npm run build`
|
||||
- Blocks not appearing → ACF Pro must be active
|
||||
- Menu not rendering → must assign menu to location in WP admin
|
||||
- JavaScript not loading → check script module support (WP 6.5+)
|
||||
|
||||
### 2. architecture.md
|
||||
|
||||
**Purpose:** Explain how the theme is organized and how it boots.
|
||||
|
||||
- Bootstrap flow: `functions.php` → glob autoload of `lib/*.php` → `init` hook → class instantiation
|
||||
- The 7 architectural layers (from knowledge graph):
|
||||
1. **Entry** — `functions.php` and `style.css` (theme declaration)
|
||||
2. **Service Layer** — PHP classes in `lib/` (Enqueue, MenuItems, Breadcrumbs, ACF, Resources)
|
||||
3. **UI Templates** — WordPress template hierarchy, ACF blocks, components, partials, icons
|
||||
4. **Styling** — Layered CSS with Tailwind v4 (base → components → blocks → navigation)
|
||||
5. **Client Scripts** — JS modules (Navigation, BackToTop, Button, GetHeaderHeight, TagExternalLinks)
|
||||
6. **Data Layer** — ACF field group JSON schemas in `acf/`
|
||||
7. **Infrastructure** — Build scripts, CI/CD pipelines, project configs, tests
|
||||
- Namespace conventions:
|
||||
- PHP namespace: `BasicWP`
|
||||
- Text domain: `basicwp`
|
||||
- Block category: `vdi-blocks`
|
||||
- WP Engine folder: `vdi-v5`
|
||||
- Explain the relationship between these naming choices
|
||||
- Global variables: `$views` (path to views/ directory) and `$theme` (theme directory path)
|
||||
- WordPress hooks cleanup in `hooks.php`: what gets removed (emojis, block library styles, REST links, XML-RPC) and why — performance and security rationale
|
||||
- Enqueue system: `wp_enqueue_script_module()` for JS (dependency graph: theme → button, backToTop, Navigation, etc.) vs traditional `wp_enqueue_script` for admin
|
||||
- theme.json design system: color palette (12 colors via CSS custom properties), font sizes (15 presets), spacing, layout
|
||||
|
||||
### 3. creating-blocks.md
|
||||
|
||||
**Purpose:** Teach a developer how to create a new ACF block from scratch.
|
||||
|
||||
- Anatomy of a block: `block.json` (registration) + PHP template (rendering) + CSS file (scoped styles)
|
||||
- Step-by-step tutorial creating a new "Testimonial" block:
|
||||
1. Create directory `views/blocks/testimonial/`
|
||||
2. Create `block.json` with required fields (name, title, description, category, icon, supports)
|
||||
3. Create `testimonial.php` template using `blockWrapperAttributes()` and `getFieldValue()`
|
||||
4. Create `testimonial.css` for block-specific styles
|
||||
5. Create ACF field group in WP admin → exported to `acf/` as JSON
|
||||
- How blocks are auto-registered: `regACFBlocks()` in `functions.php` scans `views/blocks/*/block.json`
|
||||
- Helper functions: `blockWrapperAttributes()` for class/id passthrough, `getFieldValue()` for dot-notation ACF access
|
||||
- Parent-child block patterns: buttons→button, grid→grid-cell (using `allowed_blocks` or `InnerBlocks`)
|
||||
- Tailwind in blocks: how the whitelist system works (`whitelist.php`), how to add editor-only classes
|
||||
- Block CSS loading: each block's CSS file is auto-enqueued by the block registration
|
||||
|
||||
### 4. reference.md
|
||||
|
||||
**Purpose:** Complete API and configuration reference for quick lookup.
|
||||
|
||||
- **Hooks and Filters** — Complete table of all `add_action`/`add_filter` calls from `hooks.php`, `extras.php`, `search-features.php`, and class files, with priority, callback, and purpose
|
||||
- **theme.json Design Tokens** — Color palette (12 colors with CSS variable names), font sizes (15 presets with rem values), spacing scale, layout sizes
|
||||
- **CSS Architecture** — Import tree from `theme.css`, explanation of each layer (base, components, blocks, navigation), how to add new stylesheets
|
||||
- **JS Module Dependency Graph** — Visual/text tree showing `theme.js` → Navigation, BackToTop, Button, GetHeaderHeight, TagExternalLinks; `admin.js` → Button
|
||||
- **Navigation Class API** — Methods for the sliding viewport feature (initializeSlidingViewport, navigateToLevel, navigateBack, etc.), configuration options
|
||||
- **Helper Functions** — `getFieldValue()`, `blockWrapperAttributes()`, `customMenuOrder()`, `blockCategories()`, `consoleLog()`, `customExcerpt()`, `escEmbeds()`
|
||||
- **Class Reference** — Enqueue (enqFEAssets, enqBEAssets, enqEditorAssets), ACF (saveJson, loadJson), Breadcrumbs (generate, render, and per-page-type methods), MenuItems (render), Resources (CPT + permalink rewrite), ShowTemplate (debug template path)
|
||||
- **CLI Commands** — `npm run build`, `npm run start`/`npm run watch`, `composer lint`, `composer fix`, Playwright test commands
|
||||
- **Deployment Workflow** — GitHub Actions `wpengine.yml` pipeline: install deps → build Tailwind → rsync to WP Engine
|
||||
- **Testing** — Playwright a11y test setup and commands, PHPCS configuration and usage
|
||||
|
||||
## README.md Changes
|
||||
|
||||
- Trim: Remove the detailed function-level API docs (they move to `reference.md`)
|
||||
- Trim: Remove the detailed project structure tree (keep a shorter version, point to `architecture.md` for the full picture)
|
||||
- Add: "Documentation" section with links to all four guides
|
||||
- Fix: Wrong CSS paths (`views/styles/` → `styles/`)
|
||||
- Fix: Missing `contact-info` block from the block list
|
||||
- Fix: Deployment filename typo (`wpengine,yml` → `wpengine.yml`)
|
||||
- Fix: Note that `class-enqueue.php` also has `enqEditorAssets()`
|
||||
- Add: Mention `backToTop.js` custom element (missing from current README)
|
||||
- Add: Clarify namespace conventions (BasicWP, basicwp, vdi-blocks, vdi-v5)
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
This is a documentation-only change — no code modifications. Implementation consists of:
|
||||
|
||||
1. Create `docs/` directory with 4 markdown files
|
||||
2. Update `README.md` with trimmed content and links
|
||||
3. Fix known discrepancies in README
|
||||
|
||||
Estimated scope: ~4 documentation files of ~200-400 lines each, plus README edits.
|
||||
Reference in New Issue
Block a user