🔵 other: Rename project and publish

This commit is contained in:
Keith Solomon
2026-07-29 14:25:19 -05:00
parent 5512918b0f
commit 7dbb991fb9
70 changed files with 558 additions and 5386 deletions
+2 -20
View File
@@ -1,23 +1,5 @@
{
"permissions": {
"allow": [
"Bash(mkdir -p \"C:/Users/ksolo/Projects/Work Projects/VDI-Starter-v5/.understand-anything/intermediate\")",
"Bash(mkdir -p \"C:/Users/ksolo/Projects/Work Projects/VDI-Starter-v5/.understand-anything/tmp\")",
"Bash(grep -v \"knowledge-graph.json$\")",
"Bash(npm --version)",
"Bash(npm install *)",
"Bash(pnpm --version)",
"Read(//c/Users/ksolo/.claude/plugins/cache/understand-anything/understand-anything/2.5.0/**)",
"Bash(pnpm install *)",
"Bash(pnpm --filter @understand-anything/core build)",
"Bash(node -e ' *)",
"Bash(node *)",
"Bash(python \"C:/Users/ksolo/.claude/plugins/cache/understand-anything/understand-anything/2.5.0/skills/understand/merge-batch-graphs.py\" \"C:/Users/ksolo/Projects/Work Projects/VDI-Starter-v5\")",
"Bash([ -d \"$PLUGIN_ROOT/packages/dashboard\" ])",
"Bash(GRAPH_DIR=\"C:/Users/ksolo/Projects/Work Projects/VDI-Starter-v5\" npx vite --host 127.0.0.1)",
"Bash(git add *)",
"Bash(git commit -m ' *)",
"Bash(git checkout *)"
]
"env": {
"OTEL_RESOURCE_ATTRIBUTES": "project.name=SoloFrame-Evo,project_name=SoloFrame-Evo,env=WordPress,service.namespace=development,tool=claude-code"
}
}
+2 -2
View File
@@ -59,6 +59,6 @@ jobs:
uses: wpengine/github-action-wpe-site-deploy@v3
with:
WPE_SSHG_KEY_PRIVATE: ${{ secrets.WPE_SSHG_KEY_PRIVATE }}
WPE_ENV: vdiv5
WPE_ENV: <change-me>
FLAGS: '-azvr --inplace --delete --exclude=".*"'
REMOTE_PATH: "wp-content/themes/vdi-v5"
REMOTE_PATH: "wp-content/themes/<change-me>"
-32
View File
@@ -1,32 +0,0 @@
# .understandignore — patterns for files/dirs to exclude from analysis
# Syntax: same as .gitignore (globs, # comments, ! negation, trailing / for dirs)
# Lines below are suggestions — uncomment to activate.
# Use ! prefix to force-include something excluded by defaults.
#
# Built-in defaults (always excluded unless negated):
# node_modules/, .git/, dist/, build/, obj/, *.lock, *.min.js, etc.
#
# --- From .gitignore (uncomment to exclude) ---
# *~
# .DS_Store
# static/dist/
# .env
# bak/
# testimonials.class.php
# resource-filter/
# phpcs-results.txt
# /test-results/
# /playwright-report/
# /blob-report/
# /playwright/.cache/
# --- Detected directories (uncomment to exclude) ---
# tests/
# --- Test file patterns (uncomment to exclude) ---
# *.test.*
# *.spec.*
# *.snap
-712
View File
@@ -1,712 +0,0 @@
{
".env.example": {
"size": 58,
"mtime": 1777825781884.629,
"hash": "9c87082bd45460db86334fd3acea3b44"
},
".github/workflows/phpcs.yml": {
"size": 1020,
"mtime": 1777825781886.1328,
"hash": "955988d8d00a489b15aa88a88aae21a4"
},
".github/workflows/todos.yml": {
"size": 527,
"mtime": 1777825781886.1328,
"hash": "6fda48c7bbdadcf8e1acccd906f229ee"
},
".github/workflows/wpengine.yml": {
"size": 1931,
"mtime": 1777825781886.1328,
"hash": "87dfd255f35b4b499aa94d9e42bc7091"
},
".phpcs.xml": {
"size": 2478,
"mtime": 1777825781887.1445,
"hash": "04d7057ecdc57cca79bef5866b5189d2"
},
"404.php": {
"size": 1507,
"mtime": 1777825781888.1372,
"hash": "8ab93a649e112fe2768b988cd655d0ae"
},
"README.md": {
"size": 18416,
"mtime": 1777825781888.1372,
"hash": "4f6f07adeb2af3edf17d013eb304f481"
},
"acf/group_5f7f85a2a3e13.json": {
"size": 6396,
"mtime": 1777825781889.1365,
"hash": "2a88ba2db688528090946d956e2b56ef"
},
"acf/group_5fd3e006e5da5.json": {
"size": 15908,
"mtime": 1777825781889.1365,
"hash": "a1fc1fb34007909b16644e81cba4ef81"
},
"acf/group_600f5a9e242c3.json": {
"size": 10147,
"mtime": 1777825781889.1365,
"hash": "5008ba179d87feeb1248e18d6e6ba260"
},
"acf/group_60106ed700da3.json": {
"size": 7160,
"mtime": 1777825781890.65,
"hash": "9e3e91ae651bed941daf8707ec7a9fa7"
},
"acf/group_60bfb84ae973c.json": {
"size": 5506,
"mtime": 1777825781890.65,
"hash": "0053d6b09c043521cc434eb2a3473011"
},
"acf/group_60bfdb328901d.json": {
"size": 3036,
"mtime": 1777825781890.65,
"hash": "6a8f66b681bcd266d8c1ab54ef98e2ab"
},
"acf/group_6261bc658dd80.json": {
"size": 12859,
"mtime": 1777825781891.6606,
"hash": "5d28ff3cf8dfc2e277147d6d8438aa00"
},
"acf/group_645e51f721207.json": {
"size": 4156,
"mtime": 1777825781891.6606,
"hash": "f7b6adbd69a0f366caa44332434b6dd9"
},
"acf/group_645e7cf448e66.json": {
"size": 12304,
"mtime": 1777825781891.6606,
"hash": "08d883e93358825224d87c531f364244"
},
"bin/.build.js": {
"size": 648,
"mtime": 1777825781892.6602,
"hash": "ff47325f676fff321b2d745eb0ce607b"
},
"bin/.utils.js": {
"size": 637,
"mtime": 1777825781892.6602,
"hash": "fbf52725e6516a6177d366d227bd72f4"
},
"bin/.watch.js": {
"size": 1202,
"mtime": 1777825781893.6606,
"hash": "5167fae70eb7e1ed06210194f4769f1a"
},
"composer.json": {
"size": 873,
"mtime": 1777825781893.6606,
"hash": "231db8fffc6fcb14cb591ec8c12c9c02"
},
"content/basic-wp-test-content.xml": {
"size": 791437,
"mtime": 1777825781897.214,
"hash": "39f05bef4706c4ab5dbeb8ea3145654c"
},
"footer.php": {
"size": 4240,
"mtime": 1777825781897.214,
"hash": "50fc9d771ef337378105785b21dc6fe3"
},
"front-page.php": {
"size": 351,
"mtime": 1777825781897.214,
"hash": "85b76eebf72977bdd039ed3f15bc2593"
},
"functions.php": {
"size": 1074,
"mtime": 1777825781898.2146,
"hash": "3c137712c6f7498749c913ea81c46b79"
},
"header.php": {
"size": 1695,
"mtime": 1777825781898.2146,
"hash": "2e3526258a06b3d1d4f4c6432bcb13c2"
},
"index.php": {
"size": 3695,
"mtime": 1777825781898.2146,
"hash": "433ba96ca2ecb0e280915a01d27ac190"
},
"lib/activation.php": {
"size": 7278,
"mtime": 1777825781899.7202,
"hash": "a57c588d45ad1b89e29b8bb489db43eb"
},
"lib/class-acf.php": {
"size": 1361,
"mtime": 1777825781900.2366,
"hash": "449906c43d7b6c3c3b3a09475e7d725e"
},
"lib/class-breadcrumbs.php": {
"size": 14272,
"mtime": 1777825781900.2366,
"hash": "4de39580babb14c094ca4c56768e7bab"
},
"lib/class-enqueue.php": {
"size": 4076,
"mtime": 1777825781901.2432,
"hash": "45fac2122b6afa64b814b170dcb2b30d"
},
"lib/class-menuitems.php": {
"size": 4658,
"mtime": 1777825781901.2432,
"hash": "fd0c0e87ddfcfa68ec59e0f15b50a69a"
},
"lib/class-resources.php": {
"size": 3910,
"mtime": 1777825781902.1833,
"hash": "32a8fe6eec6dd9eb29af7e367893b0de"
},
"lib/extras.php": {
"size": 6860,
"mtime": 1777825781902.1833,
"hash": "7adc58c4c81579ac00ca69c5d04e7fac"
},
"lib/helpers.php": {
"size": 6680,
"mtime": 1777825781903.1887,
"hash": "60d1207517fb0da64403cf1b7dbb6539"
},
"lib/hooks.php": {
"size": 7439,
"mtime": 1777825781903.1887,
"hash": "0f3de261772dc2bf2b5953b2c28b4005"
},
"lib/search-features.php": {
"size": 3183,
"mtime": 1777825781903.7036,
"hash": "6440cdd4de9075dc69324f9cf7930b6f"
},
"lib/show-template.php": {
"size": 4595,
"mtime": 1777825781904.219,
"hash": "ecb952d6c204358a73936af2c033d83a"
},
"package.json": {
"size": 1362,
"mtime": 1777825781905.6797,
"hash": "a4e1eac2cfb9895612d80bc21e1939e6"
},
"page.php": {
"size": 778,
"mtime": 1777825781905.6797,
"hash": "6a3fc28312789659b55abc7a32ded8c0"
},
"playwright.config.js": {
"size": 2327,
"mtime": 1777825781905.6797,
"hash": "7bf73735c56c1d2927d6a160f7aaf7fb"
},
"search.php": {
"size": 3850,
"mtime": 1777825781907.687,
"hash": "9e4ae80991d630321ac71ef0cb1d6694"
},
"sidebar-page.php": {
"size": 244,
"mtime": 1777825781907.687,
"hash": "b0373affab0db0d85986f96517639c25"
},
"sidebar.php": {
"size": 168,
"mtime": 1777825781907.687,
"hash": "360467fe7477f49e98e472f3dc4f6955"
},
"single.php": {
"size": 2146,
"mtime": 1777825781908.6858,
"hash": "965c3dee07716fafd73989c903238578"
},
"static/js/admin.js": {
"size": 237,
"mtime": 1777825781910.7087,
"hash": "13ca21b03b0f87a97b393c976fd7e1d4"
},
"static/js/components/backToTop.js": {
"size": 1081,
"mtime": 1777825781910.7087,
"hash": "f158c4c1059fef386165dae93ce3f7b0"
},
"static/js/components/button.js": {
"size": 2888,
"mtime": 1777825781911.7075,
"hash": "34c562742779794ffd99385654924054"
},
"static/js/modules/GetHeaderHeight.js": {
"size": 370,
"mtime": 1777825781911.7075,
"hash": "04e6ab8db754c70f77dfd3c6a9c55946"
},
"static/js/modules/Navigation.js": {
"size": 18649,
"mtime": 1777825781911.7075,
"hash": "1347fd5408738f67d5599c86b17c7d01"
},
"static/js/modules/TagExternalLinks.js": {
"size": 1459,
"mtime": 1777825781911.7075,
"hash": "8f87f5354de0f983a912fda1ecf690dd"
},
"static/js/theme.js": {
"size": 2365,
"mtime": 1777825781913.213,
"hash": "2e6a048774813e1bb6b203e1b3d2e4ca"
},
"style.css": {
"size": 178,
"mtime": 1777825781913.213,
"hash": "2e92dcc1e52546309822b60a46423698"
},
"styles/backend/admin.css": {
"size": 63,
"mtime": 1777825781914.2341,
"hash": "01d28aa008b74cf5218535f7c12c46ee"
},
"styles/backend/editor.css": {
"size": 1467,
"mtime": 1777825781914.2341,
"hash": "5299a07817c81cb4bad10bd194d4415c"
},
"styles/base/break-out.css": {
"size": 621,
"mtime": 1777825781915.3032,
"hash": "a37de9ae25ff01581f1480117ec09556"
},
"styles/base/colors.css": {
"size": 2163,
"mtime": 1777825781915.3032,
"hash": "95de198691c4f6cb41b0873917229493"
},
"styles/base/forms.css": {
"size": 2008,
"mtime": 1777825781915.3032,
"hash": "107aab79fea61c090f8cd48e4f00448d"
},
"styles/base/global.css": {
"size": 1918,
"mtime": 1777825781916.2947,
"hash": "ae8df6bfce807baa3475f3956cb028fc"
},
"styles/base/index.css": {
"size": 205,
"mtime": 1777825781916.2947,
"hash": "ac5dde7d13978b751ff8f42bfb3773f6"
},
"styles/base/misc.css": {
"size": 34,
"mtime": 1777825781916.2947,
"hash": "47e77d13ea40808d5dbe4c6ef32d126b"
},
"styles/base/prose.css": {
"size": 1911,
"mtime": 1777825781916.2947,
"hash": "3efddc6366c6c4fbcd6144875fb6117f"
},
"styles/base/skip-link.css": {
"size": 528,
"mtime": 1777825781917.295,
"hash": "809617c7a5f61b31e6d9e0eae13b4179"
},
"styles/base/typography.css": {
"size": 5219,
"mtime": 1777825781917.295,
"hash": "4fe5505c437c471f557ad5be946dad3c"
},
"styles/blocks/buttons.css": {
"size": 6260,
"mtime": 1777825781918.432,
"hash": "f4cba12a016e9e30fa7a24a5b7715c56"
},
"styles/blocks/core.css": {
"size": 103,
"mtime": 1777825781918.432,
"hash": "6bcbd78bf19a67b54627546b33c5d5cd"
},
"styles/blocks/index.css": {
"size": 77,
"mtime": 1777825781918.432,
"hash": "56028790f82e8ccc98c912536aa774f2"
},
"styles/components/breadcrumbs.css": {
"size": 215,
"mtime": 1777825781919.433,
"hash": "5fabf70478264c3865676e9f67cbb9fc"
},
"styles/components/index.css": {
"size": 205,
"mtime": 1777825781919.433,
"hash": "a1261bd940ead1cbcdde91c4170dc2c6"
},
"styles/components/pagination.css": {
"size": 1039,
"mtime": 1777825781920.4336,
"hash": "2ae9c16d59f4cab33a99df3065d61670"
},
"styles/components/post-list.css": {
"size": 598,
"mtime": 1777825781920.4336,
"hash": "f7f37b20749734761de3be0d6be2e282"
},
"styles/components/sidebar.css": {
"size": 505,
"mtime": 1777825781920.4336,
"hash": "b12c0335ba1e3fac4b3787675d8a6212"
},
"styles/components/site-footer.css": {
"size": 825,
"mtime": 1777825781921.4446,
"hash": "9c1d4b76f87f60cff9efa3f4b3735836"
},
"styles/components/site-header.css": {
"size": 369,
"mtime": 1777825781921.4446,
"hash": "9133b9bf75dab0c1cfc3c7cb367f867a"
},
"styles/fonts/lineicons.css": {
"size": 45564,
"mtime": 1777825781930.8862,
"hash": "e452d71b3ef8171cf944cd98e08fdc34"
},
"styles/navigation/index.css": {
"size": 1109,
"mtime": 1777825781931.898,
"hash": "28ddb2adae05fc7e68a3e60b2b1665e5"
},
"styles/navigation/nav-aux.css": {
"size": 893,
"mtime": 1777825781932.8987,
"hash": "ea8dcd8a588e5df2b1a45158ad661a47"
},
"styles/navigation/nav-footer.css": {
"size": 2117,
"mtime": 1777825781933.9001,
"hash": "8c7b0110320476f715805d325f08840a"
},
"styles/navigation/nav-functional.css": {
"size": 2272,
"mtime": 1777825781933.9001,
"hash": "afe257894f4d65a67e39ce2973cd2706"
},
"styles/navigation/nav-main-default.css": {
"size": 1638,
"mtime": 1777825781933.9001,
"hash": "96cf7a64c5f0e9bfe812f17a68f9b805"
},
"styles/navigation/nav-main-mega.css": {
"size": 2880,
"mtime": 1777825781935.4058,
"hash": "aa1d27e4635d8376be5c3a18d832df73"
},
"styles/navigation/nav-mobile-accordion.css": {
"size": 1368,
"mtime": 1777825781935.4058,
"hash": "761cfa36b712e142edac8b8fc877fd8d"
},
"styles/navigation/nav-mobile-sliding.css": {
"size": 3700,
"mtime": 1777825781936.4126,
"hash": "82fac7165c53b9cf670b96f29de109ac"
},
"styles/theme.css": {
"size": 519,
"mtime": 1777825781936.4126,
"hash": "95ea2ebd0ca96908f9091594935e0344"
},
"tests/site-a11y.spec.js": {
"size": 2066,
"mtime": 1777825781937.4146,
"hash": "4f080be14808e2da3d5b0cb220dba1f2"
},
"theme.json": {
"size": 6054,
"mtime": 1777825781937.4146,
"hash": "2a0fbef9becf6301f529a6153b688f6f"
},
"views/blocks/accordion/accordion.css": {
"size": 1528,
"mtime": 1777825781938.4128,
"hash": "56f033d708d93ad79c41c2c74c4132d2"
},
"views/blocks/accordion/accordion.php": {
"size": 1585,
"mtime": 1777825781938.4128,
"hash": "71463047c491068471355e5cb638ba6c"
},
"views/blocks/accordion/block.json": {
"size": 536,
"mtime": 1777825781939.9185,
"hash": "7a1add0f62b40208b5c4b80bcfabe92d"
},
"views/blocks/boilerplate/block.json": {
"size": 595,
"mtime": 1777825781940.9243,
"hash": "6b87816fdbd0bdfd69c9fcbbd69247d8"
},
"views/blocks/boilerplate/boilerplate.css": {
"size": 0,
"mtime": 1777825781940.9243,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/boilerplate/boilerplate.php": {
"size": 511,
"mtime": 1777825781941.8308,
"hash": "1a95084b5da0e23e9be4245c510e52e2"
},
"views/blocks/button/block.json": {
"size": 453,
"mtime": 1777825781941.8308,
"hash": "cd5d1611c7cc5de7d6e295693f3fa372"
},
"views/blocks/button/button.php": {
"size": 1492,
"mtime": 1777825781942.8354,
"hash": "84e2ddbc1ff1d2ddbcc38984adfa6df1"
},
"views/blocks/buttons/block.json": {
"size": 481,
"mtime": 1777825781942.8354,
"hash": "94cf5b9d7bf6a47efa9da9a29041dadd"
},
"views/blocks/buttons/buttons.php": {
"size": 506,
"mtime": 1777825781942.8354,
"hash": "de91ee9a77d5a81f8db4b919b43b5f0d"
},
"views/blocks/contact-info/block.json": {
"size": 636,
"mtime": 1777825781944.3481,
"hash": "2229315da64e61c2b793eae04e74710d"
},
"views/blocks/contact-info/contact-info.css": {
"size": 0,
"mtime": 1777825781944.3481,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/contact-info/contact-info.php": {
"size": 1605,
"mtime": 1777825781945.8645,
"hash": "0356348ae30b343a109fae42e2c0b533"
},
"views/blocks/grid-cell/block.json": {
"size": 520,
"mtime": 1777825781946.87,
"hash": "125ff116a0ea042fbfb5639104f42df4"
},
"views/blocks/grid-cell/grid-cell.css": {
"size": 0,
"mtime": 1777825781946.87,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/grid-cell/grid-cell.php": {
"size": 1020,
"mtime": 1777825781946.87,
"hash": "dcc8c272645da336a4090034e6963794"
},
"views/blocks/grid/block.json": {
"size": 513,
"mtime": 1777825781946.87,
"hash": "754d826d448aace6e2a5ff83a9bc4e8e"
},
"views/blocks/grid/grid.css": {
"size": 0,
"mtime": 1777825781948.7903,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/grid/grid.php": {
"size": 1139,
"mtime": 1777825781948.7903,
"hash": "c6d1d48d35dd122b21eff6fb1c64d06e"
},
"views/blocks/homepage-hero/block.json": {
"size": 541,
"mtime": 1777825781949.7954,
"hash": "6129c6269cc9d03d0bbbfbc89a508712"
},
"views/blocks/homepage-hero/homepage-hero.css": {
"size": 0,
"mtime": 1777825781949.7954,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/homepage-hero/homepage-hero.php": {
"size": 1926,
"mtime": 1777825781950.797,
"hash": "ab4084346343efab9c7ea8b039543194"
},
"views/blocks/media-text-innerblocks/block.json": {
"size": 669,
"mtime": 1777825781950.797,
"hash": "9bd57859917b5ecabf25dca938de0468"
},
"views/blocks/media-text-innerblocks/media-text-innerblocks.css": {
"size": 0,
"mtime": 1777825781951.796,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/media-text-innerblocks/media-text-innerblocks.php": {
"size": 1848,
"mtime": 1777825781951.796,
"hash": "657cfdbb55232c469a910021fe09892b"
},
"views/blocks/media-text/block.json": {
"size": 611,
"mtime": 1777825781951.796,
"hash": "3393eda2493e5f58ee720a0582c99f5c"
},
"views/blocks/media-text/media-text.css": {
"size": 0,
"mtime": 1777825781951.796,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/media-text/media-text.php": {
"size": 3574,
"mtime": 1777825781953.3013,
"hash": "f90e05790f9213186f4bdd1bdfe0b96f"
},
"views/blocks/page-children/block.json": {
"size": 606,
"mtime": 1777825781953.3013,
"hash": "188a5891ee40fd21ad29718370aedabf"
},
"views/blocks/page-children/page-children.css": {
"size": 0,
"mtime": 1777825781954.3088,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/page-children/page-children.php": {
"size": 5987,
"mtime": 1777825781954.3088,
"hash": "fe92977dce68904fb4bc06c54ff3e446"
},
"views/blocks/section/block.json": {
"size": 485,
"mtime": 1777825781954.3088,
"hash": "73a372aac3416824d5ef23db34d091bf"
},
"views/blocks/section/section.css": {
"size": 0,
"mtime": 1777825781954.3088,
"hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"views/blocks/section/section.php": {
"size": 2095,
"mtime": 1777825781955.8137,
"hash": "6e4504fd83601eb01a1f3eb873156d2f"
},
"views/components/menu-items/has-children.php": {
"size": 2813,
"mtime": 1777825781955.8137,
"hash": "1a8aa09cd21c8db30d1463a6250dc3b6"
},
"views/components/menu-items/index.php": {
"size": 935,
"mtime": 1777825781956.8318,
"hash": "526811dd0098d380edcebfcef1621e32"
},
"views/components/menu-items/single.php": {
"size": 1021,
"mtime": 1777825781956.8318,
"hash": "64be46dbd08d637d19a322e860ed8ea1"
},
"views/components/nav-aux.php": {
"size": 1043,
"mtime": 1777825781956.8318,
"hash": "0ed85cb990d7b3ae31c730723a4d6e74"
},
"views/components/nav-main.php": {
"size": 548,
"mtime": 1777825781957.8252,
"hash": "7a9fe22a0083b29d18f2dbc32077e64e"
},
"views/components/nav-main__toggle.php": {
"size": 1499,
"mtime": 1777825781958.3442,
"hash": "52bdc7efc28113c2352436618c2708f3"
},
"views/forms/search.php": {
"size": 1561,
"mtime": 1777825781958.3442,
"hash": "556adef0c6550114a3ddd7d5c5cfc39a"
},
"views/icons/facebook.php": {
"size": 31,
"mtime": 1777825781959.3416,
"hash": "aab70d132f1217d1518620198545fd51"
},
"views/icons/heart.php": {
"size": 481,
"mtime": 1777825781959.3416,
"hash": "a072424d8c330d8b39966d1a53b23c69"
},
"views/icons/instagram.php": {
"size": 32,
"mtime": 1777825781959.3416,
"hash": "419d4db42de56eb157b95c95e41b9652"
},
"views/icons/linkedin.php": {
"size": 31,
"mtime": 1777825781960.341,
"hash": "331b0e87d54b8463cfa8f23552823b8f"
},
"views/icons/menu.php": {
"size": 46,
"mtime": 1777825781960.341,
"hash": "ea55de11ec126433429a0c2ee4cfa165"
},
"views/icons/pinterest.php": {
"size": 32,
"mtime": 1777825781960.341,
"hash": "c7e3c2d8d92d5c3874bbd759bfc1b650"
},
"views/icons/search.php": {
"size": 592,
"mtime": 1777825781961.3496,
"hash": "e1dd2e58f826f18f93685d20f45fdcd9"
},
"views/icons/shop.php": {
"size": 27,
"mtime": 1777825781961.3496,
"hash": "760cac61de143a536417225f2aefbf42"
},
"views/icons/shopping-basket.php": {
"size": 803,
"mtime": 1777825781961.3496,
"hash": "5d65124138a3401017a894524361a06b"
},
"views/icons/twitter-.php": {
"size": 30,
"mtime": 1777825781962.3413,
"hash": "6c7604c7c0a3f98479668136cf4c7c8a"
},
"views/icons/twitter.php": {
"size": 24,
"mtime": 1777825781962.3413,
"hash": "c5aa48dfdbc559246297e2efc64659ca"
},
"views/icons/user.php": {
"size": 580,
"mtime": 1777825781962.3413,
"hash": "e0d22c3cde5a1b62db6fbd390cca62d2"
},
"views/icons/youtube.php": {
"size": 30,
"mtime": 1777825781962.3413,
"hash": "4b470494537d81f6b16874a639d957fd"
},
"views/partials/page-hero.php": {
"size": 1582,
"mtime": 1777825781963.5125,
"hash": "e58b6957a66d7e8ff200390630110abb"
},
"views/partials/social-media.php": {
"size": 1378,
"mtime": 1777825781963.5125,
"hash": "e9e66dfe0edd982394ca30bdcc5cc93e"
},
"whitelist.php": {
"size": 4636,
"mtime": 1777825781964.5107,
"hash": "ffaf553fec77c8899fed05522dc2102c"
}
}
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
{
"lastAnalyzedAt": "2026-05-03T17:58:02.312Z",
"gitCommitHash": "7a578c67c0c0eec7abbe4ad40a01db0e4c67aeb7",
"version": "1.0.0",
"analyzedFiles": 142
}
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Page Not Found
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
get_header();
?>
+16 -17
View File
@@ -1,14 +1,13 @@
# VDI WordPress Theme Starter v5
# SoloFrame Evo
VDI WordPress Theme Starter v5 is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.
SoloFrame Evo is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.
Repo: [https://github.com/Vincent-Design-Inc/VDI-Starter-v5](https://github.com/Vincent-Design-Inc/VDI-Starter-v5)
AC Bug/Issue Tracking: [https://next-app.activecollab.com/119590/projects/4553?modal=Task-98192-4553](https://next-app.activecollab.com/119590/projects/4553?modal=Task-98192-4553)
Repo: [https://github.com/ksolomon/SoloFrame-Evo](https://github.com/ksolomon/SoloFrame-Evo)
## Key Features
- Tailwind CSS v4 (CSS-first configuration, no `tailwind.config.js`)
- Namespaced PHP (`BasicWP` namespace) for isolation
- Namespaced PHP (`SoloFrameEvo` namespace) for isolation
- Built-in support for ACF blocks with auto-registration
- Fast development workflow with BrowserSync live reload
- WordPress 6.5+ script modules for modern JS loading
@@ -25,15 +24,15 @@ AC Bug/Issue Tracking: [https://next-app.activecollab.com/119590/projects/4553?m
## Project Structure
```
VDI-Starter-v5/
├── acf/ # ACF field group JSON definitions
```plain
SoloFrame-Evo/
├── acf/ # ACF/SCF field group JSON definitions
├── bin/ # Build scripts (.build.js, .watch.js, .utils.js)
├── content/ # Sample page and post content for testing
├── docs/ # Documentation guides
├── lib/ # PHP library files
│ ├── activation.php # Theme activation handler (plugins, pages, settings)
│ ├── class-acf.php # ACF integration (JSON sync paths)
│ ├── class-acf.php # ACF/SCF integration (JSON sync paths)
│ ├── class-breadcrumbs.php # Breadcrumb generation with Schema.org markup
│ ├── class-enqueue.php # Assets enqueuing (CSS, JS, fonts)
│ ├── class-menuitems.php # Navigation menu builder
@@ -61,7 +60,7 @@ VDI-Starter-v5/
├── tests/ # Automated Playwright tests
│ └── site-a11y.spec.js # Site Accessibility tests
├── views/ # Template views
│ ├── blocks/ # Custom ACF blocks (accordion, button, grid, section, etc.)
│ ├── blocks/ # Custom ACF/SCF blocks (accordion, button, grid, section, etc.)
│ ├── icons/ # SVG icon templates
│ ├── forms/ # Form templates (search)
│ └── partials/ # Reusable template parts (page-hero, social-media)
@@ -87,12 +86,12 @@ The project uses several naming conventions. Here's how they relate:
| Convention | Value | Where Used |
| ----------- | ------- | ----------- |
| PHP namespace | `BasicWP` | All PHP files |
| Text domain | `basicwp` | WordPress translations |
| Block category | `vdi-blocks` | Groups blocks in the editor |
| Script module IDs | `basicwp-theme`, `basicwp-button`, `basicwp-admin` | JS module registration |
| WP Engine folder | `vdi-v5` | Deployment target |
| Theme directory | `VDI-Starter-v5` | Git repo name |
| PHP namespace | `SoloFrameEvo` | All PHP files |
| Text domain | `sf-evo` | WordPress translations |
| Block category | `sf-blocks` | Groups blocks in the editor |
| Script module IDs | `sf-evo-theme`, `sf-evo-button`, `sf-evo-admin` | JS module registration |
| WP Engine folder | `<change-me>>` | Deployment target |
| Theme directory | `SoloFrame-Evo` | Git repo name |
## Included Blocks
@@ -124,7 +123,7 @@ The project uses several naming conventions. Here's how they relate:
```
4. Copy `.env.example` to `.env` and set:
- `LOCALHOST_URL`: Your local development URL (e.g., `http://localhost:1000`)
- `LOCALHOST_URL`: Your local development URL (e.g., `http://site-dev.local`)
- `BROWSERSYNC_PORT`: Port for BrowserSync (default: `5000`)
5. Run the initial build:
+41 -24
View File
@@ -1,29 +1,46 @@
# 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
- [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)
- [theme.json Design System](#themjson-design-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)
@@ -141,7 +158,7 @@ SVG icon partials for Facebook, Instagram, LinkedIn, Pinterest, Twitter, YouTube
CSS is organized as a layered cascade, managed through Tailwind CSS v4:
```
```plain
styles/theme.css ← Entry point (imports everything below)
├── @import "tailwindcss" ← Tailwind CSS v4 base
├── @import "./base/index.css" ← Base styles barrel file
@@ -183,7 +200,7 @@ 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
@@ -195,7 +212,7 @@ static/js/admin.js (entry point for editor)
└── button.js → ButtonComponent for editor context
```
**How script modules work:** WordPress's `wp_enqueue_script_module()` creates proper ES module dependencies. The `Enqueue` class registers `basicwp-theme` (theme.js) as a root module, and `basicwp-button` (button.js) declares a dependency on it. This means button.js won't load until theme.js has loaded — no more manual script ordering.
**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,7 +220,7 @@ 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 |
| ------ | ------------- |
@@ -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
@@ -249,14 +267,12 @@ 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';
```
@@ -326,8 +343,8 @@ The `Enqueue` class (in `class-enqueue.php`) manages all asset loading:
| ------- | -------- | ------- |
| `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()`)
@@ -336,8 +353,8 @@ The `Enqueue` class (in `class-enqueue.php`) manages all asset loading:
| ------- | -------- | ------- |
| 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()`)
@@ -348,7 +365,7 @@ The `Enqueue` class (in `class-enqueue.php`) manages all asset loading:
**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
+72 -48
View File
@@ -1,7 +1,9 @@
# Creating Blocks in VDI-Starter-v5
# Creating Blocks in SoloFrame Evo
## Table of Contents
- [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)
@@ -12,15 +14,25 @@
- [blockWrapperAttributes()](#blockwrapperattributes)
- [getFieldValue()](#getfieldvalue)
- [escEmbeds()](#escembeds)
- [ACF Field Groups](#acf-field-groups)
- [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 ACF Field Groups in WordPress Admin](#5-create-acf-field-groups-in-wordpress-admin)
- [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)
@@ -29,12 +41,15 @@
- [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": {
@@ -132,8 +147,8 @@ The `block.json` file tells WordPress and ACF everything they need to know about
| `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**.
@@ -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
+34 -49
View File
@@ -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
@@ -42,7 +42,7 @@ 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. |
@@ -119,7 +119,7 @@ services:
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- ./themes/vdi-starter-v5:/var/www/html/wp-content/themes/vdi-starter-v5
- ./themes/SoloFrame-Evo:/var/www/html/wp-content/themes/SoloFrame-Evo
depends_on:
- db
@@ -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:
@@ -186,15 +174,15 @@ 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` |
| ------------------- | ----------------------------------------------------------------------------------- | ------------------------------ |
| `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,9 +275,9 @@ 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
@@ -303,7 +287,7 @@ VDI-Starter-v5/
├── 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
@@ -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:
@@ -405,14 +389,14 @@ The Tailwind entry point is `styles/theme.css`. It imports sub-stylesheets using
@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 */
@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
@@ -576,7 +561,7 @@ Check `wp-content/mu-plugin-install.log` for detailed error messages. Each plugi
| `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) |
+29 -17
View File
@@ -1,13 +1,15 @@
# 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
- [SoloFrame Evo Theme Reference](#soloframe-evo-theme-reference)
- [Table of Contents](#table-of-contents)
- [Hooks and Filters](#hooks-and-filters)
- [hooks.php (BasicWP Namespace)](#hooksphp-basicwp-namespace)
- [hooks.php (SoloFrameEvo Namespace)](#hooksphp-soloframeevo-namespace)
- [extras.php](#extrasphp)
- [helpers.php](#helpersphp)
- [class-enqueue.php](#class-enqueuephp)
@@ -25,19 +27,27 @@
- [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 |
| ------ | ------ | ---------- | ------ | ------------- |
@@ -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)
@@ -97,7 +109,7 @@ Adds theme supports:
| --------------- | ------ | ---------- | ------ | ------------- |
| `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 |
---
@@ -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,7 +337,7 @@ Example -- adding a new `cards.css` component:
## JS Module Dependency Graph
```
```plain
theme.js (entry point)
├── Navigation.js # Mobile menu, sliding viewport, keyboard nav
├── backToTop.js # BackToTopButton custom element
@@ -343,15 +355,15 @@ 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');
```
---
@@ -404,7 +416,7 @@ nav.initializeSlidingViewport();
| `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`). |
@@ -491,8 +503,8 @@ The deployment workflow is defined in `.github/workflows/wpengine.yml`.
| --------- | ------- |
| 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,7 +518,7 @@ The deployment workflow is defined in `.github/workflows/wpengine.yml`.
### rsync Flags
```
```plain
-azvr --inplace --delete --exclude=".*"
```
@@ -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.
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Theme footer template
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
$footerLogo = getFieldValue( 'footer.footer_logo.url' ) ? getFieldValue( 'footer.footer_logo.url' ) : '';
$footerDesc = getFieldValue( 'footer.footer_description' ) ? getFieldValue( 'footer.footer_description' ) : '';
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Front Page Template
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
get_header();
?>
+3 -3
View File
@@ -1,15 +1,15 @@
<?php
/**
* Functions file for the BasicWP theme.
* Functions file for the SoloFrameEvo theme.
*
* This file initializes the theme by including necessary dependencies
* and loading additional function files.
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Load functions.
foreach ( glob( __DIR__ . '/lib/*.php' ) as $filename ) {
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Theme header template
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
global $views;
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Blog posts list
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
+5 -17
View File
@@ -18,11 +18,11 @@ if ( isset( $_GET['activated'] ) && is_admin() ) {
* Sample nested structure
*
* 'Parent Page' => array(
* 'Subpage 1' => array(
* 'Sub-subpage 1',
* 'Sub-subpage 2'
* 'Child page 1' => array(
* 'Grandchild page 1',
* 'Grandchild page 2'
* ),
* 'Subpage 2.2' => array()
* 'Child page 2' => array()
* ),
*/
);
@@ -105,17 +105,9 @@ if ( isset( $_GET['activated'] ) && is_admin() ) {
$muPlugins = array(
array(
'url' => 'https://docs.vincentdevelopment.ca/files/advanced-custom-fields-pro.zip',
'url' => 'https://downloads.wordpress.org/plugin/secure-custom-fields.6.9.3.zip',
'active' => true,
),
array(
'url' => 'https://docs.vincentdevelopment.ca/files/gravity-forms.zip',
'active' => true,
),
array(
'url' => 'https://updraftplus.com/wp-content/uploads/updraftplus.zip',
'active' => false,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/simple-history.5.11.0.zip',
'active' => true,
@@ -128,10 +120,6 @@ if ( isset( $_GET['activated'] ) && is_admin() ) {
'url' => 'https://downloads.wordpress.org/plugin/better-search-replace.1.4.10.zip',
'active' => true,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/google-site-kit.1.153.0.zip',
'active' => false,
),
);
// Custom log file
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* ACF (Advanced Custom Fields) support class & functions
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class ACF
+1 -1
View File
@@ -1,5 +1,5 @@
<?php
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class Breadcrumbs
+10 -10
View File
@@ -1,12 +1,12 @@
<?php
/**
* BasicWP Theme Enqueue Class
* SoloFrameEvo Theme Enqueue Class
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class Enqueue
@@ -39,7 +39,7 @@ class Enqueue {
$css_path = '/static/dist/theme.css';
if ( file_exists( $theme_dir . $css_path ) ) {
$version = filemtime( $theme_dir . $css_path );
wp_enqueue_style( 'basicwp-theme', $theme_uri . $css_path, array(), $version );
wp_enqueue_style( 'sf-evo-theme', $theme_uri . $css_path, array(), $version );
}
$font_ver = gmdate( 'U' );
@@ -53,8 +53,8 @@ class Enqueue {
if ( file_exists( $theme_dir . $js_path ) ) {
$version = filemtime( $theme_dir . $js_path );
wp_enqueue_script( 'jquery' ); // Needed by downstream scripts; modules can't depend on classic scripts.
wp_enqueue_script_module( 'basicwp-theme', $theme_uri . $js_path, array(), $version );
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-theme' ), $version );
wp_enqueue_script_module( 'sf-evo-theme', $theme_uri . $js_path, array(), $version );
wp_enqueue_script_module( 'sf-evo-button', $theme_uri . '/static/js/components/button.js', array( 'sf-evo-theme' ), $version );
}
}
@@ -74,7 +74,7 @@ class Enqueue {
$admin_css_path = '/styles/backend/admin.css';
if ( file_exists( $theme_dir . $admin_css_path ) ) {
$version = filemtime( $theme_dir . $admin_css_path );
wp_enqueue_style( 'basicwp-admin', $theme_uri . $admin_css_path, array(), $version );
wp_enqueue_style( 'sf-evo-admin', $theme_uri . $admin_css_path, array(), $version );
}
/**
@@ -84,8 +84,8 @@ class Enqueue {
if ( file_exists( $theme_dir . $admin_js_path ) ) {
$version = filemtime( $theme_dir . $admin_js_path );
wp_enqueue_script( 'jquery' ); // Needed by downstream scripts; modules can't depend on classic scripts.
wp_enqueue_script_module( 'basicwp-admin', $theme_uri . $admin_js_path, array(), $version );
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-admin' ), $version );
wp_enqueue_script_module( 'sf-evo-admin', $theme_uri . $admin_js_path, array(), $version );
wp_enqueue_script_module( 'sf-evo-button', $theme_uri . '/static/js/components/button.js', array( 'sf-evo-admin' ), $version );
}
}
@@ -103,7 +103,7 @@ class Enqueue {
if ( file_exists( $theme_dir . $editor_css_path ) ) {
$version = filemtime( $theme_dir . $editor_css_path );
wp_enqueue_style( 'basicwp-editor', $theme_uri . $editor_css_path, array(), $version );
wp_enqueue_style( 'sf-evo-editor', $theme_uri . $editor_css_path, array(), $version );
}
}
}
+3 -3
View File
@@ -1,12 +1,12 @@
<?php
/**
* BasicWP MenuItems Class
* SoloFrameEvo MenuItems Class
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class MenuItems
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Resources custom post type & taxonomies
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class Resources
+3 -3
View File
@@ -2,11 +2,11 @@
/**
* Filters. etc
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/** Get child pages of the current page, sorted by menu order.
*
@@ -154,7 +154,7 @@ function getTheTitle() {
} elseif ( is_search() ) {
$title = sprintf(
/* translators: %s is replaced with the search query */
__( 'Search Results for "%s"', 'basicwp' ),
__( 'Search Results for "%s"', 'sf-evo' ),
get_search_query()
);
} elseif ( is_404() ) {
+5 -5
View File
@@ -1,12 +1,12 @@
<?php
/**
* BasicWP Theme Helpers
* SoloFrameEvo Theme Helpers
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Define global variables for theme and views folder paths.
global $theme, $views;
@@ -122,8 +122,8 @@ add_filter( 'menu_order', __NAMESPACE__ . '\\customMenuOrder', 10, 1 );
*/
function blockCategories( $categories ) {
$vdi_cat = array(
'slug' => 'vdi-blocks',
'title' => 'VDI Custom Blocks',
'slug' => 'sf-blocks',
'title' => 'SoloFrame Custom Blocks',
'icon' => 'dashicons-admin-customizer',
);
+3 -3
View File
@@ -1,12 +1,12 @@
<?php
/**
* BasicWP Theme Hooks
* SoloFrameEvo Theme Hooks
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Add preconnect for Google fonts to head
+3 -3
View File
@@ -1,12 +1,12 @@
<?php
/**
* Search features for BasicWP theme.
* Search features for SoloFrameEvo theme.
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Modifies the WordPress query object for page search functionality.
+3 -3
View File
@@ -2,11 +2,11 @@
/**
* Add a comment to show which template is being used on the current page.
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
/**
* Class ShowTemplate
@@ -14,7 +14,7 @@ namespace BasicWP;
* Displays the active WordPress template in the footer for debugging purposes.
* Determines which template WordPress has chosen to use and outputs it as an HTML comment.
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
class ShowTemplate {
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "vdi-starter-v5",
"name": "soloframe-evo",
"version": "5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vdi-starter-v5",
"name": "soloframe-evo",
"version": "5.0",
"license": "MIT",
"dependencies": {
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "vdi-starter-v5",
"name": "soloframe-evo",
"version": "5.0",
"description": "VDI-Starter-v5 is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.",
"description": "SoloFrame Evo is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.",
"scripts": {
"start": "npm run watch",
"watch": "node bin/.watch.js",
@@ -9,7 +9,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/Vincent-Design-Inc/VDI-Starter-v5.git"
"url": "git+https://github.com/ksolomon/SoloFrame Evo.git"
},
"keywords": [
"wordpress",
@@ -21,9 +21,9 @@
"author": "Keith Solomon <keith@vincentdesign.ca>",
"license": "MIT",
"bugs": {
"url": "https://github.com/Vincent-Design-Inc/VDI-Starter-v5/issues"
"url": "https://github.com/ksolomon/SoloFrame Evo/issues"
},
"homepage": "https://github.com/Vincent-Design-Inc/VDI-Starter-v5#readme",
"homepage": "https://github.com/ksolomon/SoloFrame Evo#readme",
"devDependencies": {
"@axe-core/playwright": "^4.10.1",
"@playwright/test": "^1.52.0",
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Single Pages
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
get_header();
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Blog search template
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
+3 -3
View File
@@ -2,14 +2,14 @@
/**
* Page Sidebar
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
?>
<aside class="sidebar sidebar-page">
<h2><?php esc_html_e( 'Page Sidebar', 'basicwp' ); ?></h2>
<h2><?php esc_html_e( 'Page Sidebar', 'sf-evo' ); ?></h2>
<?php dynamic_sidebar( 'sidebar-page' ); ?>
</aside>
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Sidebar
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
?>
+2 -2
View File
@@ -2,11 +2,11 @@
/**
* Single Posts
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
get_header();
+1 -1
View File
@@ -3,5 +3,5 @@
* Description: Custom WordPress theme starter for VDI Projects
* Version: 5.0
* Author: Vincent Design Inc.
* Text Domain: basicwp
* Text Domain: sf-evo
*/
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that renders the Accordion block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$open = get_field( 'open' );
$group = get_field( 'group_items' );
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./boilerplate.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "block-default",
"keywords": [
"boilerplate"
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template for building your own custom blocks.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$classes = 'boilerplate';
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "acf/button",
"title": "Button (VDI)",
"title": "Button (SFE)",
"description": "A button.",
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "button",
"keywords": [
"button"
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Button block
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve ACF fields
$element = get_field( 'element' ) ? get_field( 'element' ) : 'a';
+2 -2
View File
@@ -1,9 +1,9 @@
{
"name": "acf/buttons",
"title": "Buttons (VDI)",
"title": "Buttons (SFE)",
"description": "A button or group of buttons.",
"allowedBlocks": [ "acf/button" ],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "button",
"keywords": [
"buttons"
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that renders the Buttons block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$ibClasses = 'flex flex-wrap gap-4 w-full justify-center sm:justify-start';
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./contact-info.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "feedback",
"keywords": [
"contact info",
+5 -5
View File
@@ -4,10 +4,10 @@
*
* Display contact information from global fields with icons and optional form.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$classes = 'contact-info';
$wrapper = blockWrapperAttributes( $classes, $is_preview );
@@ -19,13 +19,13 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
<h2 class="text-2xl font-bold mb-4">Contact Information</h2>
<div class="not-prose text-black my-4">
<h3 class="mb-0"><?php echo esc_html__( 'Mailing Address', 'basicwp' ); ?></h3>
<h3 class="mb-0"><?php echo esc_html__( 'Mailing Address', 'sf-evo' ); ?></h3>
<p class="my-0.5"><?php echo wp_kses_post( get_field( 'contact_info', 'option' )['address'] ); ?></p>
<h3 class="mb-0"><?php echo esc_html__( 'Email', 'basicwp' ); ?></h3>
<h3 class="mb-0"><?php echo esc_html__( 'Email', 'sf-evo' ); ?></h3>
<p class="my-0.5"><a class="hover:opacity-80 transition-colors duration-100" href="mailto:<?php echo esc_html( get_field( 'contact_info', 'option' )['email'] ); ?>"><?php echo esc_html( get_field( 'contact_info', 'option' )['email'] ); ?></a></p>
<h3 class="mb-0"><?php echo esc_html__( 'Phone', 'basicwp' ); ?></h3>
<h3 class="mb-0"><?php echo esc_html__( 'Phone', 'sf-evo' ); ?></h3>
<p class="my-0.5"><a class="hover:opacity-80 transition-colors duration-100" href="tel:<?php echo esc_html( get_field( 'contact_info', 'option' )['phone'] ); ?>"><?php echo esc_html( get_field( 'contact_info', 'option' )['phone'] ); ?></a></p>
</div>
</div>
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "acf/grid-cell",
"title": "Grid Cell (VDI)",
"title": "Grid Cell (SFE)",
"description": "Custom grid cell block.",
"style": [
"file:./grid-cell.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "grid-view",
"keywords": [
"grid"
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that displays the Grid Cell block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Initialize variables
$className = ! empty( $block['className'] ) ? $block['className'] : '';
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "acf/grid",
"title": "Grid Block (VDI)",
"title": "Grid Block (SFE)",
"description": "Custom grid block.",
"style": [
"file:./grid.css"
],
"allowedBlocks": [ "acf/grid-cell" ],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "grid-view",
"keywords": [
"grid"
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that displays the grid block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$allowedBlocks = array( 'acf/grid-cell' );
$default_blocks = array(
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./homepage-hero.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "block-default",
"keywords": [
"homepage-hero",
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Block Name: Homepage Hero
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve ACF fields
$heading = get_field( 'heading' );
@@ -5,7 +5,7 @@
"style": [
"file:./media-text-innerblocks.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "screenoptions",
"keywords": [
"media",
@@ -4,10 +4,10 @@
*
* This is the template that displays the media text inner blocks.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve ACF fields
$bgColor = get_field( 'background_color' ) ? get_field( 'background_color' ) : '#c5c5c5';
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./media-text.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "screenoptions",
"keywords": [
"media",
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that displays the Media With Text block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve ACF fields
$bgColor = get_field( 'background_color' ) ? get_field( 'background_color' ) : '#c5c5c5';
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./page-children.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "block-default",
"keywords": [
"page-children"
+3 -3
View File
@@ -2,10 +2,10 @@
/**
* Page Children block
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve the current page ID and its children
$parentId = get_queried_object_id();
@@ -89,7 +89,7 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
</h3>
</div>
<div class="my-0 grow-1 text-18px text-gray-600 <?php echo esc_attr( $gcPad ); ?>">
<div class="my-0 grow text-18px text-gray-600 <?php echo esc_attr( $gcPad ); ?>">
<?php
if ( ! empty( $grandchildren ) ) {
?>
+1 -1
View File
@@ -5,7 +5,7 @@
"style": [
"file:./section.css"
],
"category": "vdi-blocks",
"category": "sf-blocks",
"icon": "align-wide",
"keywords": [
"setion",
+2 -2
View File
@@ -4,10 +4,10 @@
*
* This is the template that displays the section block.
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Retrieve ACF fields
$contentWidth = get_field( 'content_width' );
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Variables available:
// $item from parent template
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Variables available from MenuItems component:
// $topLevelNavItems, $hasChildren, $nestedNavItems, $currentPage, $location
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Variables available:
// $item from parent template
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation-aux.html
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Init Variables
global $wp, $views;
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
namespace BasicWP;
namespace SoloFrameEvo;
global $views;
?>
+1 -1
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Init Variables
$navIcon = isset( get_field( 'header', 'option' )['nav_icon'] ) ? get_field( 'header', 'option' )['nav_icon'] : '';
+5 -5
View File
@@ -6,7 +6,7 @@
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/search-global.html
*/
namespace BasicWP;
namespace SoloFrameEvo;
?>
@@ -14,16 +14,16 @@ namespace BasicWP;
<form method="get" action="<?php echo esc_url( home_url( '/' ) ); ?>" class="global-search-form relative flex justify-start max-w-full w-full">
<label for="globalSearch">
<svg role="img" aria-labelledby="globalSearchLabel" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="absolute w-4 top-2 left-2 fill-primary-700">
<title id="globalSearchLabel"><?php echo esc_attr_x( 'Search', 'search-label', 'basicwp' ); ?></title>
<title id="globalSearchLabel"><?php echo esc_attr_x( 'Search', 'search-label', 'sf-evo' ); ?></title>
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/>
</svg>
</label>
<input id="globalSearch" type="search" placeholder="" value="<?php echo esc_attr( get_search_query() ); ?>" name="s" class="md:min-w-[25ch] w-full md:w-[18em] max-w-full text-sm p-1 pl-7 border-2 border-primary-700 rounded-l focus-visible:ring-2 ring-primary-700 !outline-default">
<input id="globalSearch" type="search" placeholder="" value="<?php echo esc_attr( get_search_query() ); ?>" name="s" class="md:min-w-[25ch] w-full md:w-[18em] max-w-full text-sm p-1 pl-7 border-2 border-primary-700 rounded-l focus-visible:ring-2 ring-primary-700 outline-default!">
<button type="submit" class="bg-primary rounded-r py-0 px-3 height-full text-sm text-white focus-visible:ring-2 ring-primary-700 !outline-default cursor-default">
<?php echo esc_attr_x( 'Search', 'search-submit', 'basicwp' ); ?>
<button type="submit" class="bg-primary rounded-r py-0 px-3 height-full text-sm text-white focus-visible:ring-2 ring-primary-700 outline-default! cursor-default">
<?php echo esc_attr_x( 'Search', 'search-submit', 'sf-evo' ); ?>
</button>
</form>
</search>
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Page Hero Partial
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
// Set variables
$bgColor = get_field( 'background_color' );
+2 -2
View File
@@ -2,10 +2,10 @@
/**
* Social Media Links Partial
*
* @package BasicWP
* @package SoloFrameEvo
*/
namespace BasicWP;
namespace SoloFrameEvo;
$classes = $args['classes'] ?? '';
$circle = $args['circle'] ?? '';
+2 -2
View File
@@ -6,11 +6,11 @@
* These are for use in the editor and do not appear in the theme codebase, so
* Tailwind does not know to include them without this list.
*
* @package BasicWP
* @package SoloFrameEvo
* @since 1.0.0
*/
namespace BasicWP;
namespace SoloFrameEvo;
?>