Architecture

  • JNext lesson
  • KPrevious lesson
  • FSearch lessons
  • EscClear search

Directory Structure

acf-blocks-plugin/
  acf-blocks.php              Main plugin file
  includes/
    functions.php              Block registration, styles, editor assets
    compat.php                 ACF 6.7+ compatibility layer
    image-localizer.php        External image download on save
    license-manager.php        License activation and update checks
  assets/
    js/
      block-transforms.js      Editor-side block transforms
  blocks/
    accordion-block/           One directory per block
      block.json               Block metadata (ACF v3)
      block-data.json          ACF field group definition
      accordion-block.php      PHP render template
      accordion.css            Block stylesheet
      extra.php                Optional: additional hooks
    callout/
      ...
    (27 more block directories)
  llm/
    (29 markdown files for AI/LLM reference)
  .github/
    workflows/
      release.yml              CI: tag push creates zip release

Boot Sequence

The plugin loads in this order:

The ACF Blocks architecture is deliberately flat: one main file, an includes directory for registration and compatibility, and a blocks directory where every block is a self-contained folder. Understanding this layout is most of what you need to extend or debug the plugin.

  1. acf-blocks.php runs immediately. It defines constants and instantiates the license manager (which hooks into admin_menu, admin_init, and the update system).

  2. On plugins_loaded, acf_blocks_init() checks whether ACF is available. If yes, it loads functions.php, compat.php, and image-localizer.php. If ACF is missing, it registers an admin notice and stops.

  3. On init (priority 5), acf_blocks_register_styles() pre-registers all block stylesheets with wp_register_style(). This allows WordPress to conditionally enqueue them only when a block appears on the page.

  4. On acf/init (priority 5), acf_blocks_load_blocks() scans the blocks/ directory. For each subdirectory containing a block.json, it calls register_block_type(), loads ACF field groups from any *.json files (excluding block.json), and includes extra.php if present.

  5. On block_categories_all, a custom “ACF Blocks” category is inserted at the top of the block inserter.

  6. On enqueue_block_editor_assets, the block transforms script is enqueued.

  7. Editor styles are loaded through three overlapping mechanisms (see below).

Block Category

All blocks are registered under the acf-blocks category, which appears first in the block inserter. The category is added via the block_categories_all filter.

Per-Block Structure

Every block lives in its own directory under blocks/ and contains at minimum:

  • block.json – Standard WordPress block metadata with an acf key specifying the render template and block version.
  • block-data.json – An ACF field group definition (JSON). This is loaded automatically. No manual import through the ACF admin is needed.
  • {block-name}.php – The PHP render template that receives $block, $content, $is_preview, and $post_id.
  • {block-name}.css – Block styles, referenced via "style": "file:./filename.css" in block.json.

Some blocks also include:

  • extra.php – Additional hooks, AJAX handlers, or scripts. Loaded automatically if present.
  • {block-name}.js – Frontend JavaScript (e.g. star rating submission, section block editor component).

No Build Step

The plugin has no build system, no package.json, no webpack or bundler. All JavaScript is vanilla ES5-compatible (IIFE pattern). Assets are committed as source files and deployed as-is.

Editor Style Loading

Getting block styles into the Gutenberg editor iframe is notoriously unreliable across WordPress versions and themes. The plugin uses three overlapping mechanisms to ensure styles always appear:

  1. add_editor_style() via after_setup_theme (priority 999) – Registers all block CSS files as editor styles.

  2. wp_enqueue_style() via enqueue_block_assets (priority 999999, admin only) – Directly enqueues all block stylesheets in the editor.

  3. Inline CSS injection via block_editor_settings_all (priority 999999) – Reads all CSS files and injects them as inline styles into the editor settings object.

On the frontend, styles are loaded conditionally. WordPress only enqueues a block’s stylesheet when that block appears on the page, because the styles are pre-registered (not enqueued) during init.

CSS Class Naming

All blocks use the acf- prefix for CSS classes: .acf-accordion, .acf-hero-block, .acf-product-review, etc. This prevents conflicts with theme and plugin styles.

Quick answers to common questions:

Where does each block live in the plugin?

Under blocks/{block-name}/ with its block.json, field group JSON, and render template together. Nothing is scattered: deleting a block folder removes that block cleanly, and copying one is the starting point for a custom block.

Can I override a block’s template from my theme?

Yes, the render pipeline checks your theme for an override before falling back to the plugin’s template, so customizations survive plugin updates. The render templates lesson covers the exact lookup order.