Native and ACF block support with auto‑registration
Chisel lets you build faster with blocks. Our theme auto‑discovers and registers both native and ACF blocks on boot, so you ship features – not boilerplate. Drop your block assets and Twig templates in place and the system handles the rest: assets enqueued, categories organized, templates wired, and smart defaults applied. Editors get a polished block library under a branded category, while developers enjoy a zero‑friction workflow.
Under the hood, a lightweight factory ininc/WP/Blocks.phpdoes the heavy lifting—collecting definitions, registering blocks on init, and exposing their Twig locations to Timber for consistent rendering. The result is clean, predictable markup across the board, powered by Twig and aligned with your design system.
Performance is baked in. Only the styles for blocks actually used on a page are kept; the rest are dequeued, with optional critical CSS inlined for instant paint. Whether your blocks compile tobuild/blocks/or ACF counterparts inbuild/blocks-acf/, the experience is seamless: one convention, one rendering path, and a faster site – out of the box.
Overview
- Native and ACF blocks are compiled to
build/blocks/andbuild/blocks-acf/. - At runtime, both are auto‑discovered and registered via a factory:
Chisel\Factory\RegisterBlocks. - Twig templates for blocks are resolved from their source folders in
src/, so you can keep PHP/Twig close to the block code.
Key classes and paths:
inc/WP/Blocks.php— native blocks lifecycleinc/WP/AcfBlocks.php— ACF blocks lifecycleinc/Factory/RegisterBlocks.php— discovery + registration for both typessrc/blocks/— native blocks (e.g.,src/blocks/accordion/)src/blocks-acf/— ACF blocks (e.g.,src/blocks-acf/slider/)build/blocks/andbuild/blocks-acf/— compiled block assets withblock.json
Native blocks lifecycle
- Bootstrap:
Chisel\WP\Blockshooksinit→ register_blocks() calls the factory to register all native blocks- Adds a custom block category at the top of the inserter
block_categories_all→ block_categories() using theme name
- Adds custom block pattern categories
- register_block_patterns_categories()
- Extends Twig lookup paths for block templates
timber/locations→ tiwg_files_locations() addssrc/blocks/{block}/
- Post‑render content tweaks
- render_block() removes layout classes and adds custom classes via
Chisel\Helper\BlocksHelpers::get_block_object_classnames()
- render_block() removes layout classes and adds custom classes via
- Performance helpers (covered later)
- Dequeues unused block styles on a page and adds “critical” inline CSS if available
File: inc/WP/Blocks.php
ACF blocks lifecycle
- Bootstrap:
Chisel\WP\AcfBlockshooksacf/init→ register_blocks() calls the factory to register all ACF blocks- Extends Twig lookup paths for block templates
timber/locations→ tiwg_files_locations() addssrc/blocks-acf/{block}/
- ACF JSON integration for per‑block field groups
acf/settings/load_json→ each block’sacf-jsonfolder is added as a load pathacf/settings/save_json→ when saving a field group assigned to a block, it writes to that block’sacf-jsonfolder
- Performance helper parallel to native blocks (dequeue unused, inline “critical” CSS when present)
File: inc/WP/AcfBlocks.php
block.json expectations
- The factory supports file references in
block.jsonlike:"style": "file:./style.css""editorScript": ["file:./editor.js", "file:./extra.js"]
- For each referenced file:
- Styles →
wp_register_style( handle, url, [], version ) - Scripts →
wp_register_script( handle, url, dependencies, version, args )- Dependencies loaded from accompanying
.asset.phpif present
- Dependencies loaded from accompanying
- Styles →
See: inc/Factory/RegisterBlocks.php::register_custom_blocks()
Twig templates
- Both
Chisel\WP\BlocksandChisel\WP\AcfBlocksadd block source directories to Timber’s locations:src/blocks/{block-name}/src/blocks-acf/{block-name}/
- This allows Twig files shipped in
src/**/to be discovered at runtime, while assets come frombuild/**/.
Adding a new native block
- Create folder:
src/blocks/{block-name}/ - Add your block code:
- JS/CSS built to
build/blocks/{block-name}/ block.jsoninbuild/blocks/{block-name}/(generated by your build step)
- JS/CSS built to
- Reference files in
block.jsonusingfile:./...(style/script/editorScript/viewScript, etc.) - Run build:
npm run build(ornpm run devduring development) - The block will be auto‑discovered on next
init.
Adding a new ACF block
- Create folder:
src/blocks-acf/{your-block}/ - Add your block source; build produces
build/blocks-acf/{your-block}/block.json - Create ACF field group and assign it to the block:
- The theme routes
acf-jsonsave/load tosrc/blocks-acf/{your-block}/acf-jsonautomatically.
- The theme routes
- Run build; the block is auto‑discovered/registered on
acf/init.
Tip: Keep Twig templates in the corresponding /{block-name}/{block-name}.twig folder so Timber can locate them automatically.
Editor UX enhancements
- Custom block category named after the theme (slug:
chisel-blocks) - Optional block pattern categories (under
chisel-patterns/*) - Default alignment for certain blocks can be injected to the editor data
- Example:
Chisel\WP\Blocks::blocks_alignment_data()setschisel/sliderdefault tofull
- Example:
Block scripts/styles in block.json (with concrete mapping)
This explains each scripts/styles field in a block’s block.json and how source filenames in src
produce the files you reference in build:
assets/example-blocks/blocks/example/(source reference)- Actual theme blocks live under
src/blocks/{block}/and compile tobuild/blocks/{block}/or for ACF blocks live undersrc/blocks-acf/{block}/and compile tobuild/blocks-acf/{block}/
Key reference files:
- block.json
- index.js (imports ./style.scss)
- edit.js (imports ./editor.scss)
- script.js (imports ./style.scss)
- view.js (imports ./view.scss)
block.json fields (what they do)
In block.json:
{
"ignoreScripts": ["script"],
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": ["file:./style-index.css", "file:./style-script.css"],
"script": "file:./script.js",
"viewScript": "file:./view.js",
"viewStyle": "file:./view.css"
}JSON- editorScript
- JS loaded in the editor only.
- Example points to
file:./index.js. - Source:
index.jsis your editor entry file. - Build outputs:
index.js(+index.asset.phpfor dependencies/version).
- editorStyle
- CSS loaded in the editor only.
- Example points to
file:./index.css. - Source:
index.jsimportseditor.scss. - Build outputs
index.css.
- style
- CSS loaded in both editor and frontend (the “shared” styles).
- The example shows an array:
file:./style-index.css— produced because index.js imports ./style.scss.file:./style-script.css— produced because script.js imports ./style.scss.
- Why two files: each entry that imports style.scss emits its own “
style-{entry}.css” file. If you import the same SCSS from multiple entries, you’ll get multiple output CSS files.
- script
- JS loaded in both editor and frontend.
- Example points to
file:./script.js. - Source:
script.js - Build outputs
script.js
- viewScript
- JS loaded on the frontend only (not in the editor).
- Example points to
file:./view.js. - Source:
view.js. - Build outputs
view.js
- viewStyle
- CSS loaded on the frontend only.
- Example points to
file:./view.css. - Source:
view.scss - Build outputs
view.css.
- ignoreScripts
- Array of script keys you want to build but NOT enqueue as scripts (useful when a JS file exists purely to import SCSS).
- Example:
["script"]prevents enqueuing the script handle forscript.js, but CSS fromscript.js(i.e.,style-script.css) still builds and can be referenced under “style”. - From
inc/Factory/RegisterBlocks.php: scripts in ignoreScripts are not registered in production; during dev, they may still be registered to support live reload.
Where to place files in your theme
- Put block source under
src/blocks/{block-name}/or undersrc/blocks-acf/{block-name}/****for ACF blocks- index.js (editor entry; imports style.scss)
- edit.js (imports editor.scss)
- script.js (shared entry; can import style.scss)
- view.js (frontend entry; can import view.scss)
- style.scss, editor.scss, view.scss
- After build (
npm run build), you’ll get inbuild/blocks/{block-name}/or in/build/blocks-acf/{block-name}/for ACF block- block.json
- index.js,
index.asset.php,index.css - script.js,
script.asset.php,style-script.css style-index.css- view.js,
view.asset.php,view.css
Use file:./{built-filename} in
block.json to reference these outputs.
Arrays vs strings in block.json
- All script/style fields accept either a string or an array.
- Use arrays when multiple outputs need to be enqueued for the same key (e.g.,
"style": ["file:./style-index.css", "file:./style-script.css"]).
Handles and dependencies (implementation detail)
- The factory (
inc/Factory/RegisterBlocks.php) generates handles like:block-{type}-{blockName}-{key}Examples:block-wp-example-styleblock-wp-example-editorScript
- For JS entries, WordPress dependencies and versions come from
.asset.phpfiles generated by the build (e.g.,index.asset.php).
Tip: If a JS file exists just to import SCSS, add its key to ignoreScripts to avoid loading an empty script tag.
Critical CSS for blocks: critical.scss
What it is
- You can co-locate a
critical.scsswith your block and import it fromscript.js. - It compiles to a special CSS file named
script.cssin the block’s build folder. - On the frontend, when the block is present on the page, the theme will inline the contents of that
script.cssinto the main stylesheet handle. This optimizes above-the-fold rendering.
How to use it in your block
- In your block source (native or ACF), in
src/blocks/{block}/script.jsorsrc/blocks-acf/{block}/script.js:- Import shared styles (optional):
import './style.scss'→ buildsstyle-script.css - Import critical styles:
import './critical.scss'→ buildsscript.css(the critical bundle)
- Import shared styles (optional):
When it is inlined
- Only on the frontend (not in wp-admin).
- Only if the block is used on the current page:
get_content_blocks_names()is used to detect block usage.
- The theme then:
- Dequeues unused block styles for blocks not present.
- If
script.cssexists for a used block, it fetches it and inlines it:BlocksHelpers::get_block_inline_css($blocks_url, $block_name)→ reads.../{block}/script.csswp_add_inline_style( AssetsHelpers::get_final_handle('main'), $css )
- Inline size limit:
- Filtered via
chisel_styles_inline_size_limit(default set to 10000) inBlocks::styles_inline_size_limit().
- Filtered via
Practical guidance
- Keep
critical.scssminimal: above‑the‑fold essentials only (fonts, layout primitives, key colors). - Scope strictly to your block to avoid bleed:
- Example:
.b-your-block { ... }
- Example:
- Do not import critical.scss from editor-only entries; import it from script.js so it builds to
script.css. - If script.js exists only to import SCSS and has no runtime JS, add
"script"to"ignoreScripts"in block.json to avoid enqueuing the JS:- You still get:
style-script.css→ add it under"style"in block.jsonscript.css→ inlined automatically when the block is present
- You still get:
Notes and troubleshooting
- If a block does not appear:
- Ensure
build/blocks/{block}/block.json(orbuild/blocks-acf/...) exists. - Confirm
file:./...references point to files actually present after build.
- Ensure
- During dev, scripts may be registered even when “ignored” in metadata to enable live reload:
- See
ThemeHelpers::is_fast_refresh()logic in the factory.
- See
- If Twig is not rendering:
- Check that the Twig template lives under the source path the class adds to Timber (
src/blocks/...,src/blocks-acf/...).
- Check that the Twig template lives under the source path the class adds to Timber (