diff --git a/inc/plugins/class-atomic-wind-blocks.php b/inc/plugins/class-atomic-wind-blocks.php index 9f99f4746..1ac4cd425 100644 --- a/inc/plugins/class-atomic-wind-blocks.php +++ b/inc/plugins/class-atomic-wind-blocks.php @@ -22,6 +22,27 @@ class Atomic_Wind_Blocks { */ private static $in_query = false; + /** + * Expected Atomic Wind blocks by post ID. + * + * @var array + */ + private $expected = array(); + + /** + * Rendered Atomic Wind blocks by post ID. ID 0 is unknown. + * + * @var array + */ + private $rendered = array(); + + /** + * Hashes of inlined CSS. + * + * @var array + */ + private $inlined = array(); + /** * Initialize the module. * @@ -47,7 +68,8 @@ public function run() { add_action( 'enqueue_block_assets', array( $this, 'enqueue_base_css' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_icons_data' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor_assets' ) ); - add_action( 'wp_enqueue_scripts', array( $this, 'output_cached_css' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'output_singular_css' ), 11 ); + add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_style_builder' ) ); add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_frontend_animations' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_frontend_states' ) ); @@ -55,6 +77,8 @@ public function run() { add_filter( 'render_block', array( $this, 'render_post_fields' ), 20, 2 ); add_filter( 'render_block', array( $this, 'render_animation_attrs' ), 10, 2 ); add_filter( 'render_block', array( $this, 'render_state_attrs' ), 10, 2 ); + add_filter( 'render_block', array( $this, 'track_rendered_blocks' ), 10, 2 ); + add_action( 'wp_footer', array( $this, 'output_late_css' ), 19 ); add_action( 'template_redirect', array( $this, 'maybe_render_css_warm_page' ), 0 ); add_action( 'save_post', array( $this, 'clear_cached_css' ) ); add_filter( 'block_categories_all', array( $this, 'register_category' ) ); @@ -161,18 +185,24 @@ public function enqueue_tailwind_generator() { * @return void */ public function enqueue_base_css() { + $css = '[class*="wp-block-atomic-wind-"]{margin:0;max-width:unset;}[class*="wp-block-atomic-wind-"] p{margin:0;}'; + + // Always reset frontend blocks, including those outside the main query. + if ( ! is_admin() ) { + wp_register_style( 'atomic-wind-base', false, [], OTTER_BLOCKS_VERSION ); + wp_add_inline_style( 'atomic-wind-base', $css ); + return; + } + global $post; - + if ( ! $post || ! $this->post_has_atomic_wind_blocks( $post ) ) { return; } - + wp_register_style( 'atomic-wind-base', false, [], OTTER_BLOCKS_VERSION ); wp_enqueue_style( 'atomic-wind-base' ); - $css = '[class*="wp-block-atomic-wind-"]{margin:0;max-width:unset;}[class*="wp-block-atomic-wind-"] p{margin:0;}'; - if ( is_admin() ) { - $css .= '.editor-styles-wrapper .wp-block[class*="wp-block-atomic-wind-"]{margin:0;max-width:unset;}.editor-styles-wrapper [class*="wp-block-atomic-wind-"] p{margin:0;}'; - } + $css .= '.editor-styles-wrapper .wp-block[class*="wp-block-atomic-wind-"]{margin:0;max-width:unset;}.editor-styles-wrapper [class*="wp-block-atomic-wind-"] p{margin:0;}'; wp_add_inline_style( 'atomic-wind-base', $css ); } @@ -252,69 +282,189 @@ public function enqueue_editor_assets() { } /** - * Output cached Tailwind CSS or enqueue the generator + style-builder. + * Enqueue the frontend Tailwind fallback. * * @return void */ - public function output_cached_css() { - global $post; + private function enqueue_generator() { + $generator_asset = $this->build_path() . '/tailwind-generator-frontend.asset.php'; - if ( ! $post ) { + if ( ! file_exists( $generator_asset ) ) { return; } - if ( ! $this->post_has_atomic_wind_blocks( $post ) ) { + $gen = include $generator_asset; + wp_enqueue_script( + 'atomic-wind-tailwind-generator', + $this->plugin_url( 'build/atomic-wind/tailwind-generator-frontend.js' ), + $gen['dependencies'], + $gen['version'], + true + ); + } + + /** + * Load the queried post's CSS in the head on singular views. + * + * The post is guaranteed to render, so inlining its cached CSS early avoids + * a flash of unstyled content for the main content. Everything else (hooked + * layouts, embeds) stays on the render-tracked footer path. + * + * @return void + */ + public function output_singular_css() { + if ( ! is_singular() ) { return; } - $cached_css = get_post_meta( $post->ID, '_atomic_wind_css', true ); + $queried = get_queried_object(); - if ( $cached_css ) { - wp_register_style( 'atomic-wind-tailwind', false, [], OTTER_BLOCKS_VERSION ); - wp_enqueue_style( 'atomic-wind-tailwind' ); - wp_add_inline_style( 'atomic-wind-tailwind', $cached_css ); + if ( ! $queried instanceof \WP_Post || ! $this->post_has_atomic_wind_blocks( $queried ) ) { return; } - $generator_asset = $this->build_path() . '/tailwind-generator-frontend.asset.php'; + $this->expected[ $queried->ID ] = substr_count( $queried->post_content, '