Native and ACF block support with auto‑registration
Chisel auto-discovers and registers both native (Gutenberg) and ACF blocks at runtime, eliminating boilerplate. Drop your block code in
src/blocks/orsrc/blocks-acf/, run the build, and blocks appear in the editor—no manual registration needed.
The system handles asset enqueueing, Twig template discovery, block categories, and performance optimization automatically. Scripts and styles are registered from block.json file references, with optional critical CSS inlined for instant paint. Unused block styles are dequeued per page to keep your site fast.
Under the hood,RegisterBlocksfactory scans compiled blocks inbuild/blocks/andbuild/blocks-acf/, registers them via WordPress APIs, and wires Twig rendering through Timber. One convention, zero configuration, clean markup—out of the box.
Overview
Chisel provides automatic block discovery and registration for both native Gutenberg blocks and ACF blocks. The system compiles blocks from source folders to build directories, then discovers and registers them at runtime.
Key directories
| Path | Purpose |
|---|---|
src/blocks/ | Native block source code |
build/blocks/ | Compiled native blocks with block.json |
src/blocks-acf/ | ACF block source code |
build/blocks-acf/ | Compiled ACF blocks with block.json |
Core classes
core/WP/Blocks.php— Native blocks lifecycle and hookscore/WP/AcfBlocks.php— ACF blocks lifecycle and ACF JSON integrationcore/Factories/RegisterBlocks.php— Block discovery and registration factorycore/Helpers/BlocksHelpers.php— Block rendering utilities and critical CSS handling
Auto-discovery workflow
Native blocks
File: core/WP/Blocks.php
Initialization on init hook:
RegisterBlocksfactory scansbuild/blocks/directory- Discovers all subdirectories containing
block.jsonfiles - Registers each block via
register_block_type() - Processes asset references (
file:./...) inblock.json - Registers scripts/styles with proper handles and dependencies
Additional hooks:
block_categories_all→ Adds custom block category at top of inserter (e.g., “Chisel Blocks”)timber/locations→ Extends Twig template locations to includesrc/blocks/{block-name}/render_block→ Adds custom BEM classes to block wrappers, removes layout classeswp_print_styles→ Dequeues unused block styles and inlines critical CSS
ACF blocks
File: core/WP/AcfBlocks.php
Initialization on acf/init hook:
RegisterBlocksfactory scansbuild/blocks-acf/directory- Discovers all ACF block
block.jsonfiles - Registers each ACF block via
register_block_type() - Processes assets same way as native blocks
ACF JSON integration:
acf/settings/load_json→ Addssrc/blocks-acf/{block}/acf-jsonas load path for each blockacf/settings/save_json→ Automatically saves field groups to block’sacf-jsonfolder- Keeps field definitions co-located with block code
Block registration factory
File: core/Factories/RegisterBlocks.php
The factory accepts 'wp' or 'acf' as block type and handles all registration logic.
Constructor responsibilities
- Sets blocks folder based on type:
blocks/orblocks-acf/ - Defines paths:
src/{blocks-folder}/,build/{blocks-folder}/ - Scans for blocks via
DirectoryIterator - Stores blocks list for registration
Registration process
Method: register_custom_blocks()
For each discovered block:
- Checks if
block.jsonexists inbuild/{blocks-folder}/{block-name}/ - Decodes
block.jsonto extract metadata - Processes script/style fields:
editorScript,editorStyle,style,script,viewScript,viewStyle - For each
file:./...reference: - Calls
register_block_type()with path and modified metadata - Includes optional
init.phpfrom block source folder if present
Script handle format
Handles follow pattern: block-{type}-{blockName}-{key}
Examples:
block-wp-accordion-styleblock-acf-slider-viewScript
Block.json file structure
Script and style fields
All fields accept a string or array of file references using file:./... syntax.
| Field | When loaded | Purpose |
|---|---|---|
editorScript | Editor only | JS for block registration and edit component |
editorStyle | Editor only | CSS for block appearance in editor |
style | Editor and frontend | Shared CSS for both contexts |
script | Editor and frontend | Shared JS for both contexts |
viewScript | Frontend only | JS for frontend interactivity |
viewStyle | Frontend only | CSS for frontend-only styling |
viewScriptModule | Frontend only | ES module for frontend (registered via wp_register_script_module()) |
Special properties
render — PHP callback for server-side rendering (not needed with Timber/Twig)
ignoreScripts — Array of script keys to build but not enqueue (useful when JS file only imports SCSS)
Example block.json
{
"apiVersion": 3,
"name": "chisel/accordion",
"title": "Accordion",
"category": "chisel-blocks",
"icon": "editor-justify",
"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",
"ignoreScripts": ["script"]
}JSONSource file structure
Native block structure
Location: src/blocks/{block-name}/
Typical files:
index.js— Editor entry (importsedit.js,style.scss)edit.js— Block edit component (importseditor.scss)script.js— Shared entry (can importstyle.scss,critical.scss)view.js— Frontend entry (importsview.scss)style.scss— Shared styleseditor.scss— Editor-only stylesview.scss— Frontend-only stylescritical.scss— Critical/above-fold styles (optional)render.twigor{block-name}.twig— Twig templateinit.php— Optional PHP initialization logic
ACF block structure
Location: src/blocks-acf/{block-name}/
Similar to native blocks, plus:
{block-name}.twig— Twig template (matches block slug)acf-json/— ACF field group JSON files- Field groups assigned to block auto-save here
Build outputs
After running npm run build, webpack compiles blocks to build/blocks/ or build/blocks-acf/:
Generated files
From index.js:
index.js— Compiled editor scriptindex.asset.php— Dependencies and versionindex.css— Compiled editor styles
From script.js:
script.js— Compiled shared scriptscript.asset.php— Dependencies and versionstyle-script.css— Compiled shared styles (fromstyle.scssimport)script.css— Critical CSS (fromcritical.scssimport)
From view.js:
view.js— Compiled frontend scriptview.asset.php— Dependencies and versionview.css— Compiled frontend styles
Plus:
block.json— Copied/generated block metadata{block-name}.twig— Copied Twig template
Twig template rendering
Native blocks
File: core/Helpers/BlocksHelpers.php
Method: render_twig_file()
Renders Twig templates from build/blocks/{block-name}/render.twig . Timber automatically discovers templates because Blocks::tiwg_files_locations() adds src/blocks/{block-name}/ to search paths .
ACF blocks
Method: acf_block_render()
Context variables provided:
block— Block data (name, ID, attributes, align, className)post_id— Current post IDslug— Block slug withb-prefixis_preview— Whether rendering in editorfields— ACF field values fromget_fields()block['class_names']— Array of block classesblock['block_id']— Block anchor or IDwrapper_attributes— String of wrapper attributes for direct output
Filters available:
chisel_timber_acf_blocks_data— Global filter for all ACF blockschisel_timber_acf_blocks_data_{slug}— Block-specific filterchisel_timber_acf_blocks_data_{block_id}— Instance-specific filter
Renders: build/blocks-acf/{block-slug}/{block-slug}.twig
Editor customizations
Custom block category
File: core/WP/Blocks.php
Method: block_categories()
Adds custom category at top of block inserter:
- Slug:
chisel-blocks - Title:
{Theme Name} Blocks - Icon: Chisel logo SVG
Block pattern categories
Method: register_block_patterns_categories()
Default categories:
chisel-patterns/cta— Call to Actionchisel-patterns/features— Features Sectionschisel-patterns/hero— Hero Sections
Categories prefixed with theme name in labels .
Block alignment defaults
Method: blocks_alignment_data()
Sets default alignment for specific blocks via chisel_editor_scripts filter:
$editor_scripts_data['editor']['localize']['data']['blocksDefaultAlignment'] = array(
'chisel/slider' => 'full',
);PHPPerformance optimizations
Dequeue unused block styles
Files: core/WP/Blocks.php, core/WP/AcfBlocks.php
Method: dequeue_blocks_styles()
On wp_print_styles (frontend only):
- Detects blocks used on current page via
get_content_blocks_names() - Dequeues style handles for blocks not present
- Reduces CSS payload significantly
Critical CSS inlining
Method: BlocksHelpers::get_block_inline_css()
For blocks present on page:
- Checks if
{block}/script.cssexists (built fromcritical.scss) - Fetches CSS content via
wp_remote_get() - Inlines via
wp_add_inline_style()to main stylesheet handle
Size limit: Configurable via chisel_styles_inline_size_limit filter (default: 10,000 bytes)
Separate core block assets
Filter: chisel_load_separate_core_block_assets
Controls whether WordPress loads core block styles separately or in single block-library.css (default: false for better performance) .
Fast refresh support
Condition: ThemeHelpers::is_fast_refresh()
In development mode, scripts in ignoreScripts are still registered to support live reload.
Block rendering modifications
File: core/WP/Blocks.php
Method: render_block()
Post-processes block HTML output:
- Adds custom BEM classes via
BlocksHelpers::get_block_object_classnames() - Format:
c-block c-block--{namespace} c-block--{name} - Removes WordPress layout classes:
is-layout-flow,is-layout-constrained - Adds
u-table-responsivetocore/tableblocks
Adding a new native block
- Create block folder:
src/blocks/{block-name}/ - Add source files:
index.js,edit.js,style.scss,editor.scss, etc. - Create Twig template:
{block-name}.twigorrender.twig - Run build:
npm run build(ornpm run dev) - Block auto-registers on next page load
Optional: Add init.php for server-side logic
Adding a new ACF block
- Create block folder:
src/blocks-acf/{block-name}/ - Add source files: Same as native blocks
- Create Twig template:
{block-name}.twig(must match block slug) - Run build:
npm run build - Create ACF field group in WordPress admin
- Assign to block: Set location rule to
Block is equal to chisel/{block-name} - Field group saves to
src/blocks-acf/{block-name}/acf-json/automatically - Block auto-registers on
acf/init
Critical CSS workflow
Setup
In script.js
import './style.scss'; // Builds to style-script.css
import './critical.scss'; // Builds to script.css (critical)JavaScriptIn block.json
{
"style": ["file:./style-script.css"],
"ignoreScripts": ["script"]
}JSONWhat happens
style-script.css— Enqueued normally (add tostylearray)script.css— Fetched and inlined automatically when block is presentscript.js— Not enqueued (inignoreScripts) but still builds
Best practices
- Keep
critical.scssminimal (fonts, layout primitives, above-fold essentials) - Scope strictly to your block:
.b-your-block { ... } - Don’t import
critical.scssfrom editor-only entries - Monitor inline size (limit: 10KB by default)
Advanced customization
Custom script registration args
Filter: chisel_block_register_script_args
add_filter('chisel_block_register_script_args', function($args, $handle, $block) {
$args['strategy'] = 'async'; // Change from default 'defer'
return $args;
}, 10, 3);PHPDisable custom script registration
Filter: chisel_blocks_register_scripts
Set to false to let WordPress handle asset registration entirely from block.json without custom handles.
Block-specific init logic
Create init.php in block source folder:
File: src/blocks/{block-name}/init.php
// All variables from register_custom_blocks() available
// $block_name, $block_path, $block_metadata, etc.
add_filter('block_type_metadata', function($metadata) use ($block_name) {
// Modify block metadata before registration
return $metadata;
});PHPTroubleshooting
Block doesn’t appear
- Verify
build/blocks/{block}/block.jsonexists after build - Check
file:./...references point to actual files - Confirm block category exists (
chisel-blocksfor custom blocks) - Review browser console for registration errors
Twig template not found
- Ensure template is in
src/blocks/{block}/orsrc/blocks-acf/{block}/ - Timber looks in source folders, not build folders
- Template name should match block slug for ACF blocks
Styles not loading
- Check if block is in
ignoreScriptsbut not instylearray - Verify webpack compiled SCSS to CSS in build folder
- Confirm handle not dequeued by custom code
ACF field group not saving to block folder
- Ensure location rule is
Block is equal to chisel/{block-name} - Verify
acf-jsonfolder exists insrc/blocks-acf/{block-name}/ - Check folder permissions
Critical CSS not inlining
- Confirm
critical.scssimported inscript.js - Verify
script.cssexists inbuild/{block}/after build - Check inline size limit (default 10KB)
- Ensure block is actually used on current page