gutenberg.php

I’ll build this as a production-style inc/integrations/gutenberg.php for your RX theme: block editor supports, editor assets, theme presets, patterns/categories, block styles, render filters, security/performance controls, and safe fallbacks.

Below is an advanced, long, production-style inc/integrations/gutenberg.php file for your RX Theme.

Create this file:

rx-theme/inc/integrations/gutenberg.php

Then load it from functions.php:

require_once get_template_directory() . '/inc/integrations/gutenberg.php';

inc/integrations/gutenberg.php

<?php
/**
 * RX Theme Gutenberg / Block Editor Integration
 *
 * File: inc/integrations/gutenberg.php
 *
 * Features included:
 * - Gutenberg theme support
 * - Block editor styles
 * - Wide/full alignment
 * - Responsive embeds
 * - Editor color palette
 * - Editor font sizes
 * - Custom spacing support
 * - Custom line height
 * - Custom units
 * - Appearance tools
 * - Link color support
 * - Border support
 * - Editor asset loading
 * - Frontend block asset loading
 * - Block style registrations
 * - Block pattern categories
 * - Custom block patterns
 * - Optional core pattern disabling
 * - Allowed block control
 * - Block editor settings customization
 * - Render block enhancements
 * - Lazy loading support
 * - External link handling
 * - Table of contents helper
 * - Anchor helper
 * - Code block improvements
 * - Image block improvements
 * - Button rel/target helper
 * - Safe CSS class injection
 * - Admin body/editor body classes
 * - Reusable utility functions
 *
 * @package RX_Theme
 */

defined( 'ABSPATH' ) || exit;

if ( ! class_exists( 'RX_Gutenberg_Integration' ) ) :

final class RX_Gutenberg_Integration {

	/**
	 * Theme prefix.
	 *
	 * @var string
	 */
	const PREFIX = 'rx';

	/**
	 * Theme textdomain.
	 *
	 * @var string
	 */
	const TEXT_DOMAIN = 'rx-theme';

	/**
	 * Init hooks.
	 *
	 * @return void
	 */
	public static function init() {
		add_action( 'after_setup_theme', array( __CLASS__, 'theme_supports' ), 20 );
		add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_editor_assets' ) );
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_frontend_block_assets' ) );

		add_action( 'init', array( __CLASS__, 'register_block_styles' ), 20 );
		add_action( 'init', array( __CLASS__, 'register_pattern_categories' ), 20 );
		add_action( 'init', array( __CLASS__, 'register_block_patterns' ), 30 );

		add_filter( 'block_categories_all', array( __CLASS__, 'register_custom_block_categories' ), 10, 2 );
		add_filter( 'allowed_block_types_all', array( __CLASS__, 'allowed_block_types' ), 10, 2 );
		add_filter( 'block_editor_settings_all', array( __CLASS__, 'editor_settings' ), 10, 2 );
		add_filter( 'admin_body_class', array( __CLASS__, 'admin_body_class' ) );

		add_filter( 'render_block', array( __CLASS__, 'render_block_enhancements' ), 20, 2 );
		add_filter( 'render_block_core/image', array( __CLASS__, 'enhance_image_block' ), 20, 2 );
		add_filter( 'render_block_core/button', array( __CLASS__, 'enhance_button_block' ), 20, 2 );
		add_filter( 'render_block_core/paragraph', array( __CLASS__, 'enhance_paragraph_block' ), 20, 2 );
		add_filter( 'render_block_core/heading', array( __CLASS__, 'enhance_heading_block' ), 20, 2 );
		add_filter( 'render_block_core/table', array( __CLASS__, 'enhance_table_block' ), 20, 2 );
		add_filter( 'render_block_core/code', array( __CLASS__, 'enhance_code_block' ), 20, 2 );
		add_filter( 'render_block_core/group', array( __CLASS__, 'enhance_group_block' ), 20, 2 );

		add_filter( 'excerpt_allowed_blocks', array( __CLASS__, 'excerpt_allowed_blocks' ) );

		add_action( 'enqueue_block_assets', array( __CLASS__, 'enqueue_shared_block_assets' ) );
	}

	/**
	 * Gutenberg and editor theme supports.
	 *
	 * @return void
	 */
	public static function theme_supports() {

		/**
		 * Core Gutenberg supports.
		 */
		add_theme_support( 'align-wide' );
		add_theme_support( 'responsive-embeds' );
		add_theme_support( 'wp-block-styles' );
		add_theme_support( 'editor-styles' );
		add_theme_support( 'custom-line-height' );
		add_theme_support( 'custom-spacing' );
		add_theme_support( 'custom-units', array( 'px', 'em', 'rem', '%', 'vw', 'vh' ) );
		add_theme_support( 'appearance-tools' );
		add_theme_support( 'border' );
		add_theme_support( 'link-color' );
		add_theme_support( 'disable-custom-colors', false );
		add_theme_support( 'disable-custom-font-sizes', false );
		add_theme_support( 'disable-custom-gradients', false );

		/**
		 * Editor stylesheet.
		 * Create this file if you want:
		 * assets/css/editor-style.css
		 */
		add_editor_style( array(
			'style.css',
			'assets/css/editor-style.css',
		) );

		/**
		 * Editor color palette.
		 */
		add_theme_support(
			'editor-color-palette',
			array(
				array(
					'name'  => esc_html__( 'RX Primary', self::TEXT_DOMAIN ),
					'slug'  => 'rx-primary',
					'color' => '#2563eb',
				),
				array(
					'name'  => esc_html__( 'RX Primary Dark', self::TEXT_DOMAIN ),
					'slug'  => 'rx-primary-dark',
					'color' => '#1e40af',
				),
				array(
					'name'  => esc_html__( 'RX Secondary', self::TEXT_DOMAIN ),
					'slug'  => 'rx-secondary',
					'color' => '#14b8a6',
				),
				array(
					'name'  => esc_html__( 'RX Accent', self::TEXT_DOMAIN ),
					'slug'  => 'rx-accent',
					'color' => '#f97316',
				),
				array(
					'name'  => esc_html__( 'RX Success', self::TEXT_DOMAIN ),
					'slug'  => 'rx-success',
					'color' => '#16a34a',
				),
				array(
					'name'  => esc_html__( 'RX Warning', self::TEXT_DOMAIN ),
					'slug'  => 'rx-warning',
					'color' => '#f59e0b',
				),
				array(
					'name'  => esc_html__( 'RX Danger', self::TEXT_DOMAIN ),
					'slug'  => 'rx-danger',
					'color' => '#dc2626',
				),
				array(
					'name'  => esc_html__( 'RX Dark', self::TEXT_DOMAIN ),
					'slug'  => 'rx-dark',
					'color' => '#111827',
				),
				array(
					'name'  => esc_html__( 'RX Gray', self::TEXT_DOMAIN ),
					'slug'  => 'rx-gray',
					'color' => '#6b7280',
				),
				array(
					'name'  => esc_html__( 'RX Light Gray', self::TEXT_DOMAIN ),
					'slug'  => 'rx-light-gray',
					'color' => '#f3f4f6',
				),
				array(
					'name'  => esc_html__( 'RX White', self::TEXT_DOMAIN ),
					'slug'  => 'rx-white',
					'color' => '#ffffff',
				),
				array(
					'name'  => esc_html__( 'RX Black', self::TEXT_DOMAIN ),
					'slug'  => 'rx-black',
					'color' => '#000000',
				),
			)
		);

		/**
		 * Editor gradient presets.
		 */
		add_theme_support(
			'editor-gradient-presets',
			array(
				array(
					'name'     => esc_html__( 'RX Blue Gradient', self::TEXT_DOMAIN ),
					'gradient' => 'linear-gradient(135deg, #2563eb 0%, #14b8a6 100%)',
					'slug'     => 'rx-blue-gradient',
				),
				array(
					'name'     => esc_html__( 'RX Medical Gradient', self::TEXT_DOMAIN ),
					'gradient' => 'linear-gradient(135deg, #0ea5e9 0%, #16a34a 100%)',
					'slug'     => 'rx-medical-gradient',
				),
				array(
					'name'     => esc_html__( 'RX Sunset Gradient', self::TEXT_DOMAIN ),
					'gradient' => 'linear-gradient(135deg, #f97316 0%, #dc2626 100%)',
					'slug'     => 'rx-sunset-gradient',
				),
				array(
					'name'     => esc_html__( 'RX Dark Gradient', self::TEXT_DOMAIN ),
					'gradient' => 'linear-gradient(135deg, #111827 0%, #374151 100%)',
					'slug'     => 'rx-dark-gradient',
				),
			)
		);

		/**
		 * Editor font sizes.
		 */
		add_theme_support(
			'editor-font-sizes',
			array(
				array(
					'name' => esc_html__( 'Tiny', self::TEXT_DOMAIN ),
					'size' => 12,
					'slug' => 'tiny',
				),
				array(
					'name' => esc_html__( 'Small', self::TEXT_DOMAIN ),
					'size' => 14,
					'slug' => 'small',
				),
				array(
					'name' => esc_html__( 'Normal', self::TEXT_DOMAIN ),
					'size' => 16,
					'slug' => 'normal',
				),
				array(
					'name' => esc_html__( 'Medium', self::TEXT_DOMAIN ),
					'size' => 18,
					'slug' => 'medium',
				),
				array(
					'name' => esc_html__( 'Large', self::TEXT_DOMAIN ),
					'size' => 22,
					'slug' => 'large',
				),
				array(
					'name' => esc_html__( 'Extra Large', self::TEXT_DOMAIN ),
					'size' => 28,
					'slug' => 'x-large',
				),
				array(
					'name' => esc_html__( 'Huge', self::TEXT_DOMAIN ),
					'size' => 36,
					'slug' => 'huge',
				),
				array(
					'name' => esc_html__( 'Hero', self::TEXT_DOMAIN ),
					'size' => 48,
					'slug' => 'hero',
				),
			)
		);
	}

	/**
	 * Enqueue block editor-only assets.
	 *
	 * @return void
	 */
	public static function enqueue_editor_assets() {
		$theme_version = self::theme_version();

		$editor_css = get_template_directory() . '/assets/css/gutenberg-editor.css';
		$editor_js  = get_template_directory() . '/assets/js/gutenberg-editor.js';

		if ( file_exists( $editor_css ) ) {
			wp_enqueue_style(
				'rx-gutenberg-editor',
				get_template_directory_uri() . '/assets/css/gutenberg-editor.css',
				array(),
				$theme_version
			);
		}

		if ( file_exists( $editor_js ) ) {
			wp_enqueue_script(
				'rx-gutenberg-editor',
				get_template_directory_uri() . '/assets/js/gutenberg-editor.js',
				array( 'wp-blocks', 'wp-dom-ready', 'wp-edit-post', 'wp-element', 'wp-components', 'wp-i18n' ),
				$theme_version,
				true
			);
		}

		wp_add_inline_style( 'wp-edit-blocks', self::editor_inline_css() );
	}

	/**
	 * Enqueue frontend block assets.
	 *
	 * @return void
	 */
	public static function enqueue_frontend_block_assets() {
		if ( is_admin() ) {
			return;
		}

		$theme_version = self::theme_version();

		$frontend_css = get_template_directory() . '/assets/css/gutenberg-frontend.css';
		$frontend_js  = get_template_directory() . '/assets/js/gutenberg-frontend.js';

		if ( file_exists( $frontend_css ) ) {
			wp_enqueue_style(
				'rx-gutenberg-frontend',
				get_template_directory_uri() . '/assets/css/gutenberg-frontend.css',
				array(),
				$theme_version
			);
		}

		if ( file_exists( $frontend_js ) ) {
			wp_enqueue_script(
				'rx-gutenberg-frontend',
				get_template_directory_uri() . '/assets/js/gutenberg-frontend.js',
				array(),
				$theme_version,
				true
			);
		}
	}

	/**
	 * Shared assets for frontend and editor.
	 *
	 * @return void
	 */
	public static function enqueue_shared_block_assets() {
		$theme_version = self::theme_version();

		$shared_css = get_template_directory() . '/assets/css/gutenberg-shared.css';

		if ( file_exists( $shared_css ) ) {
			wp_enqueue_style(
				'rx-gutenberg-shared',
				get_template_directory_uri() . '/assets/css/gutenberg-shared.css',
				array(),
				$theme_version
			);
		}
	}

	/**
	 * Register block styles.
	 *
	 * @return void
	 */
	public static function register_block_styles() {
		if ( ! function_exists( 'register_block_style' ) ) {
			return;
		}

		$styles = array(
			'core/paragraph' => array(
				array(
					'name'  => 'rx-lead',
					'label' => esc_html__( 'RX Lead Text', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-muted',
					'label' => esc_html__( 'RX Muted Text', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-note',
					'label' => esc_html__( 'RX Note', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-warning-note',
					'label' => esc_html__( 'RX Warning Note', self::TEXT_DOMAIN ),
				),
			),

			'core/heading' => array(
				array(
					'name'  => 'rx-heading-line',
					'label' => esc_html__( 'RX Line Heading', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-heading-badge',
					'label' => esc_html__( 'RX Badge Heading', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-gradient-heading',
					'label' => esc_html__( 'RX Gradient Heading', self::TEXT_DOMAIN ),
				),
			),

			'core/image' => array(
				array(
					'name'  => 'rx-rounded',
					'label' => esc_html__( 'RX Rounded', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-shadow',
					'label' => esc_html__( 'RX Shadow', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-frame',
					'label' => esc_html__( 'RX Frame', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-polaroid',
					'label' => esc_html__( 'RX Polaroid', self::TEXT_DOMAIN ),
				),
			),

			'core/gallery' => array(
				array(
					'name'  => 'rx-gallery-card',
					'label' => esc_html__( 'RX Gallery Card', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-gallery-gap-large',
					'label' => esc_html__( 'RX Large Gap', self::TEXT_DOMAIN ),
				),
			),

			'core/quote' => array(
				array(
					'name'  => 'rx-modern-quote',
					'label' => esc_html__( 'RX Modern Quote', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-medical-quote',
					'label' => esc_html__( 'RX Medical Quote', self::TEXT_DOMAIN ),
				),
			),

			'core/pullquote' => array(
				array(
					'name'  => 'rx-pullquote-card',
					'label' => esc_html__( 'RX Pullquote Card', self::TEXT_DOMAIN ),
				),
			),

			'core/list' => array(
				array(
					'name'  => 'rx-check-list',
					'label' => esc_html__( 'RX Check List', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-arrow-list',
					'label' => esc_html__( 'RX Arrow List', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-medical-list',
					'label' => esc_html__( 'RX Medical List', self::TEXT_DOMAIN ),
				),
			),

			'core/table' => array(
				array(
					'name'  => 'rx-responsive-table',
					'label' => esc_html__( 'RX Responsive Table', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-clean-table',
					'label' => esc_html__( 'RX Clean Table', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-medical-table',
					'label' => esc_html__( 'RX Medical Table', self::TEXT_DOMAIN ),
				),
			),

			'core/button' => array(
				array(
					'name'  => 'rx-button-primary',
					'label' => esc_html__( 'RX Primary', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-button-secondary',
					'label' => esc_html__( 'RX Secondary', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-button-outline',
					'label' => esc_html__( 'RX Outline', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-button-glow',
					'label' => esc_html__( 'RX Glow', self::TEXT_DOMAIN ),
				),
			),

			'core/buttons' => array(
				array(
					'name'  => 'rx-buttons-stack-mobile',
					'label' => esc_html__( 'RX Stack Mobile', self::TEXT_DOMAIN ),
				),
			),

			'core/group' => array(
				array(
					'name'  => 'rx-card',
					'label' => esc_html__( 'RX Card', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-soft-card',
					'label' => esc_html__( 'RX Soft Card', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-glass-card',
					'label' => esc_html__( 'RX Glass Card', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-medical-box',
					'label' => esc_html__( 'RX Medical Box', self::TEXT_DOMAIN ),
				),
			),

			'core/columns' => array(
				array(
					'name'  => 'rx-equal-height',
					'label' => esc_html__( 'RX Equal Height', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-card-columns',
					'label' => esc_html__( 'RX Card Columns', self::TEXT_DOMAIN ),
				),
			),

			'core/separator' => array(
				array(
					'name'  => 'rx-dots',
					'label' => esc_html__( 'RX Dots', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-gradient-line',
					'label' => esc_html__( 'RX Gradient Line', self::TEXT_DOMAIN ),
				),
			),

			'core/code' => array(
				array(
					'name'  => 'rx-code-dark',
					'label' => esc_html__( 'RX Code Dark', self::TEXT_DOMAIN ),
				),
				array(
					'name'  => 'rx-code-terminal',
					'label' => esc_html__( 'RX Terminal', self::TEXT_DOMAIN ),
				),
			),

			'core/preformatted' => array(
				array(
					'name'  => 'rx-preformatted-card',
					'label' => esc_html__( 'RX Preformatted Card', self::TEXT_DOMAIN ),
				),
			),
		);

		foreach ( $styles as $block_name => $block_styles ) {
			foreach ( $block_styles as $style ) {
				register_block_style( $block_name, $style );
			}
		}
	}

	/**
	 * Register block pattern categories.
	 *
	 * @return void
	 */
	public static function register_pattern_categories() {
		if ( ! function_exists( 'register_block_pattern_category' ) ) {
			return;
		}

		$categories = array(
			'rx-hero' => array(
				'label'       => esc_html__( 'RX Hero Sections', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'Hero sections for medical, blog, and landing pages.', self::TEXT_DOMAIN ),
			),
			'rx-medical' => array(
				'label'       => esc_html__( 'RX Medical Sections', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'Medical content blocks and evidence-based content layouts.', self::TEXT_DOMAIN ),
			),
			'rx-content' => array(
				'label'       => esc_html__( 'RX Content Sections', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'Content blocks for articles and pages.', self::TEXT_DOMAIN ),
			),
			'rx-cta' => array(
				'label'       => esc_html__( 'RX Call To Action', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'CTA layouts for buttons, newsletter, and conversion sections.', self::TEXT_DOMAIN ),
			),
			'rx-faq' => array(
				'label'       => esc_html__( 'RX FAQ Sections', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'FAQ and question-answer layouts.', self::TEXT_DOMAIN ),
			),
		);

		foreach ( $categories as $slug => $args ) {
			register_block_pattern_category( $slug, $args );
		}
	}

	/**
	 * Register custom block categories in inserter.
	 *
	 * @param array  $categories Existing categories.
	 * @param object $post Current post.
	 *
	 * @return array
	 */
	public static function register_custom_block_categories( $categories, $post ) {
		$custom_categories = array(
			array(
				'slug'  => 'rx-blocks',
				'title' => esc_html__( 'RX Blocks', self::TEXT_DOMAIN ),
				'icon'  => 'superhero',
			),
			array(
				'slug'  => 'rx-medical-blocks',
				'title' => esc_html__( 'RX Medical Blocks', self::TEXT_DOMAIN ),
				'icon'  => 'heart',
			),
			array(
				'slug'  => 'rx-layout-blocks',
				'title' => esc_html__( 'RX Layout Blocks', self::TEXT_DOMAIN ),
				'icon'  => 'layout',
			),
		);

		return array_merge( $custom_categories, $categories );
	}

	/**
	 * Register block patterns.
	 *
	 * @return void
	 */
	public static function register_block_patterns() {
		if ( ! function_exists( 'register_block_pattern' ) ) {
			return;
		}

		register_block_pattern(
			'rx/medical-article-intro',
			array(
				'title'       => esc_html__( 'RX Medical Article Intro', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'A strong introduction pattern for evidence-based medical articles.', self::TEXT_DOMAIN ),
				'categories'  => array( 'rx-medical', 'rx-content' ),
				'keywords'    => array( 'medical', 'article', 'intro', 'rx' ),
				'content'     => self::pattern_medical_article_intro(),
			)
		);

		register_block_pattern(
			'rx/evidence-summary-box',
			array(
				'title'       => esc_html__( 'RX Evidence Summary Box', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'A highlighted summary box for evidence-based content.', self::TEXT_DOMAIN ),
				'categories'  => array( 'rx-medical', 'rx-content' ),
				'keywords'    => array( 'evidence', 'summary', 'medical', 'rx' ),
				'content'     => self::pattern_evidence_summary_box(),
			)
		);

		register_block_pattern(
			'rx/faq-section',
			array(
				'title'       => esc_html__( 'RX FAQ Section', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'Simple FAQ layout for medical and educational content.', self::TEXT_DOMAIN ),
				'categories'  => array( 'rx-faq', 'rx-content' ),
				'keywords'    => array( 'faq', 'questions', 'answers', 'rx' ),
				'content'     => self::pattern_faq_section(),
			)
		);

		register_block_pattern(
			'rx/cta-box',
			array(
				'title'       => esc_html__( 'RX CTA Box', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'A clean call-to-action box.', self::TEXT_DOMAIN ),
				'categories'  => array( 'rx-cta' ),
				'keywords'    => array( 'cta', 'button', 'action', 'rx' ),
				'content'     => self::pattern_cta_box(),
			)
		);

		register_block_pattern(
			'rx/two-column-medical-info',
			array(
				'title'       => esc_html__( 'RX Two Column Medical Info', self::TEXT_DOMAIN ),
				'description' => esc_html__( 'Two-column layout for causes, symptoms, diagnosis, and treatment sections.', self::TEXT_DOMAIN ),
				'categories'  => array( 'rx-medical', 'rx-content' ),
				'keywords'    => array( 'medical', 'columns', 'info', 'rx' ),
				'content'     => self::pattern_two_column_medical_info(),
			)
		);
	}

	/**
	 * Control allowed blocks.
	 *
	 * Return true to allow all.
	 * You can customize by post type.
	 *
	 * @param bool|array $allowed_block_types Allowed blocks.
	 * @param object     $block_editor_context Editor context.
	 *
	 * @return bool|array
	 */
	public static function allowed_block_types( $allowed_block_types, $block_editor_context ) {

		/**
		 * Default: allow all blocks.
		 *
		 * To restrict blocks, set this constant in wp-config.php:
		 * define( 'RX_RESTRICT_GUTENBERG_BLOCKS', true );
		 */
		if ( ! defined( 'RX_RESTRICT_GUTENBERG_BLOCKS' ) || ! RX_RESTRICT_GUTENBERG_BLOCKS ) {
			return true;
		}

		$post_type = '';

		if ( isset( $block_editor_context->post ) && $block_editor_context->post instanceof WP_Post ) {
			$post_type = $block_editor_context->post->post_type;
		}

		$common_blocks = array(
			'core/paragraph',
			'core/heading',
			'core/list',
			'core/list-item',
			'core/image',
			'core/gallery',
			'core/quote',
			'core/pullquote',
			'core/table',
			'core/buttons',
			'core/button',
			'core/separator',
			'core/spacer',
			'core/group',
			'core/columns',
			'core/column',
			'core/cover',
			'core/media-text',
			'core/embed',
			'core/html',
			'core/code',
			'core/preformatted',
			'core/details',
		);

		if ( 'post' === $post_type ) {
			return array_merge(
				$common_blocks,
				array(
					'core/categories',
					'core/latest-posts',
					'core/tag-cloud',
					'core/post-title',
					'core/post-date',
					'core/post-excerpt',
					'core/post-featured-image',
					'core/post-content',
				)
			);
		}

		if ( 'page' === $post_type ) {
			return array_merge(
				$common_blocks,
				array(
					'core/navigation',
					'core/site-logo',
					'core/site-title',
					'core/site-tagline',
				)
			);
		}

		return $common_blocks;
	}

	/**
	 * Customize editor settings.
	 *
	 * @param array   $settings Editor settings.
	 * @param WP_Post $post Current post.
	 *
	 * @return array
	 */
	public static function editor_settings( $settings, $post ) {

		$settings['rxTheme'] = array(
			'name'        => 'RX Theme',
			'version'     => self::theme_version(),
			'supportsSEO' => true,
			'supportsTOC' => true,
		);

		/**
		 * Recommended editor defaults.
		 */
		$settings['alignWide'] = true;

		/**
		 * Helpful custom spacing sizes for block editor.
		 */
		if ( empty( $settings['spacingSizes'] ) ) {
			$settings['spacingSizes'] = array(
				array(
					'name' => esc_html__( 'RX XS', self::TEXT_DOMAIN ),
					'slug' => 'rx-xs',
					'size' => '0.5rem',
				),
				array(
					'name' => esc_html__( 'RX SM', self::TEXT_DOMAIN ),
					'slug' => 'rx-sm',
					'size' => '1rem',
				),
				array(
					'name' => esc_html__( 'RX MD', self::TEXT_DOMAIN ),
					'slug' => 'rx-md',
					'size' => '1.5rem',
				),
				array(
					'name' => esc_html__( 'RX LG', self::TEXT_DOMAIN ),
					'slug' => 'rx-lg',
					'size' => '2rem',
				),
				array(
					'name' => esc_html__( 'RX XL', self::TEXT_DOMAIN ),
					'slug' => 'rx-xl',
					'size' => '3rem',
				),
				array(
					'name' => esc_html__( 'RX XXL', self::TEXT_DOMAIN ),
					'slug' => 'rx-xxl',
					'size' => '5rem',
				),
			);
		}

		return $settings;
	}

	/**
	 * Add admin body classes.
	 *
	 * @param string $classes Admin body classes.
	 *
	 * @return string
	 */
	public static function admin_body_class( $classes ) {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

		if ( $screen && method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() ) {
			$classes .= ' rx-block-editor rx-theme-editor ';
		}

		return $classes;
	}

	/**
	 * General render block enhancements.
	 *
	 * @param string $block_content Rendered block HTML.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function render_block_enhancements( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() || wp_is_json_request() ) {
			return $block_content;
		}

		$block_name = isset( $block['blockName'] ) ? $block['blockName'] : '';

		if ( empty( $block_name ) ) {
			return $block_content;
		}

		/**
		 * Add safe data attribute to all core blocks.
		 */
		if ( 0 === strpos( $block_name, 'core/' ) ) {
			$block_content = self::add_data_attribute_to_first_tag(
				$block_content,
				'data-rx-block',
				esc_attr( str_replace( 'core/', '', $block_name ) )
			);
		}

		return $block_content;
	}

	/**
	 * Enhance image block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_image_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		$block_content = self::add_img_attribute( $block_content, 'loading', 'lazy' );
		$block_content = self::add_img_attribute( $block_content, 'decoding', 'async' );

		if ( false === strpos( $block_content, 'data-rx-image' ) ) {
			$block_content = self::add_data_attribute_to_first_tag( $block_content, 'data-rx-image', 'true' );
		}

		return $block_content;
	}

	/**
	 * Enhance button block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_button_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		/**
		 * Add noopener to target blank links.
		 */
		if ( false !== strpos( $block_content, 'target="_blank"' ) && false === strpos( $block_content, 'rel=' ) ) {
			$block_content = str_replace(
				'target="_blank"',
				'target="_blank" rel="noopener noreferrer"',
				$block_content
			);
		}

		return $block_content;
	}

	/**
	 * Enhance paragraph block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_paragraph_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		return self::add_class_to_first_tag( $block_content, 'rx-paragraph' );
	}

	/**
	 * Enhance heading block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_heading_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		$block_content = self::add_class_to_first_tag( $block_content, 'rx-heading' );

		/**
		 * Auto add anchor from heading text if missing.
		 * This helps table of contents and deep links.
		 */
		if ( false === strpos( $block_content, ' id=' ) ) {
			$text = wp_strip_all_tags( $block_content );

			if ( ! empty( $text ) ) {
				$id = sanitize_title( $text );
				$block_content = preg_replace(
					'/(<h[1-6]\b)([^>]*>)/i',
					'$1 id="' . esc_attr( $id ) . '"$2',
					$block_content,
					1
				);
			}
		}

		return $block_content;
	}

	/**
	 * Enhance table block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_table_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		if ( false === strpos( $block_content, 'rx-table-wrap' ) ) {
			$block_content = '<div class="rx-table-wrap" data-rx-table-wrap="true">' . $block_content . '</div>';
		}

		return $block_content;
	}

	/**
	 * Enhance code block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_code_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		$block_content = self::add_class_to_first_tag( $block_content, 'rx-code-block' );

		if ( false === strpos( $block_content, 'data-rx-code' ) ) {
			$block_content = self::add_data_attribute_to_first_tag( $block_content, 'data-rx-code', 'true' );
		}

		return $block_content;
	}

	/**
	 * Enhance group block.
	 *
	 * @param string $block_content Block content.
	 * @param array  $block Block data.
	 *
	 * @return string
	 */
	public static function enhance_group_block( $block_content, $block ) {
		if ( empty( $block_content ) || is_admin() ) {
			return $block_content;
		}

		return self::add_class_to_first_tag( $block_content, 'rx-group-block' );
	}

	/**
	 * Excerpt allowed blocks.
	 *
	 * @param array $allowed_blocks Allowed excerpt blocks.
	 *
	 * @return array
	 */
	public static function excerpt_allowed_blocks( $allowed_blocks ) {
		$extra = array(
			'core/paragraph',
			'core/heading',
			'core/list',
			'core/quote',
			'core/pullquote',
			'core/table',
		);

		return array_values( array_unique( array_merge( $allowed_blocks, $extra ) ) );
	}

	/**
	 * Inline CSS for editor and registered styles.
	 *
	 * @return string
	 */
	private static function editor_inline_css() {
		return '
			.editor-styles-wrapper {
				font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
				color: #111827;
				background: #ffffff;
			}

			.editor-styles-wrapper .wp-block {
				max-width: 780px;
			}

			.editor-styles-wrapper .wp-block[data-align="wide"] {
				max-width: 1100px;
			}

			.editor-styles-wrapper .wp-block[data-align="full"] {
				max-width: none;
			}

			.editor-styles-wrapper h1,
			.editor-styles-wrapper h2,
			.editor-styles-wrapper h3,
			.editor-styles-wrapper h4,
			.editor-styles-wrapper h5,
			.editor-styles-wrapper h6 {
				font-weight: 800;
				line-height: 1.2;
				color: #111827;
			}

			.is-style-rx-lead {
				font-size: 1.25rem;
				line-height: 1.8;
				color: #374151;
			}

			.is-style-rx-muted {
				color: #6b7280;
			}

			.is-style-rx-note,
			.is-style-rx-warning-note {
				padding: 1rem 1.25rem;
				border-radius: 12px;
				border-left: 4px solid #2563eb;
				background: #eff6ff;
			}

			.is-style-rx-warning-note {
				border-left-color: #f59e0b;
				background: #fffbeb;
			}

			.is-style-rx-heading-line {
				border-bottom: 3px solid #2563eb;
				padding-bottom: .45rem;
			}

			.is-style-rx-heading-badge {
				display: inline-block;
				padding: .35rem .65rem;
				border-radius: 999px;
				background: #eff6ff;
				color: #1e40af;
			}

			.is-style-rx-gradient-heading {
				background: linear-gradient(135deg, #2563eb, #14b8a6);
				-webkit-background-clip: text;
				background-clip: text;
				color: transparent;
			}

			.is-style-rx-rounded img {
				border-radius: 18px;
			}

			.is-style-rx-shadow img {
				box-shadow: 0 20px 40px rgba(15, 23, 42, .14);
			}

			.is-style-rx-frame img {
				border: 8px solid #ffffff;
				box-shadow: 0 12px 30px rgba(15, 23, 42, .12);
			}

			.is-style-rx-polaroid {
				padding: 12px;
				background: #ffffff;
				box-shadow: 0 15px 35px rgba(15, 23, 42, .14);
				transform: rotate(-1deg);
			}

			.is-style-rx-modern-quote {
				border-left: 5px solid #2563eb;
				padding: 1.25rem 1.5rem;
				background: #f8fafc;
				border-radius: 14px;
			}

			.is-style-rx-medical-quote {
				border-left: 5px solid #14b8a6;
				padding: 1.25rem 1.5rem;
				background: #f0fdfa;
				border-radius: 14px;
			}

			.is-style-rx-check-list li::marker {
				color: #16a34a;
			}

			.is-style-rx-arrow-list li::marker {
				color: #2563eb;
			}

			.is-style-rx-medical-list {
				background: #f8fafc;
				padding: 1rem 1.25rem 1rem 2rem;
				border-radius: 14px;
			}

			.is-style-rx-responsive-table table,
			.is-style-rx-clean-table table,
			.is-style-rx-medical-table table {
				width: 100%;
				border-collapse: collapse;
			}

			.is-style-rx-clean-table table th,
			.is-style-rx-clean-table table td,
			.is-style-rx-medical-table table th,
			.is-style-rx-medical-table table td {
				border: 1px solid #e5e7eb;
				padding: .85rem;
			}

			.is-style-rx-medical-table table th {
				background: #eff6ff;
				color: #1e40af;
			}

			.is-style-rx-button-primary .wp-block-button__link {
				background: #2563eb;
				color: #ffffff;
			}

			.is-style-rx-button-secondary .wp-block-button__link {
				background: #14b8a6;
				color: #ffffff;
			}

			.is-style-rx-button-outline .wp-block-button__link {
				background: transparent;
				color: #2563eb;
				border: 2px solid #2563eb;
			}

			.is-style-rx-button-glow .wp-block-button__link {
				background: #2563eb;
				color: #ffffff;
				box-shadow: 0 10px 25px rgba(37, 99, 235, .35);
			}

			.is-style-rx-card,
			.is-style-rx-soft-card,
			.is-style-rx-glass-card,
			.is-style-rx-medical-box {
				padding: 1.5rem;
				border-radius: 18px;
			}

			.is-style-rx-card {
				background: #ffffff;
				border: 1px solid #e5e7eb;
				box-shadow: 0 10px 30px rgba(15, 23, 42, .08);
			}

			.is-style-rx-soft-card {
				background: #f8fafc;
			}

			.is-style-rx-glass-card {
				background: rgba(255, 255, 255, .72);
				backdrop-filter: blur(12px);
				border: 1px solid rgba(255,255,255,.4);
			}

			.is-style-rx-medical-box {
				background: #f0fdfa;
				border: 1px solid #99f6e4;
			}

			.is-style-rx-dots {
				border: 0;
				border-top: 4px dotted #2563eb;
				background: transparent;
			}

			.is-style-rx-gradient-line {
				height: 4px;
				border: 0;
				background: linear-gradient(135deg, #2563eb, #14b8a6);
			}

			.is-style-rx-code-dark,
			.is-style-rx-code-terminal {
				background: #111827;
				color: #e5e7eb;
				border-radius: 14px;
				padding: 1rem;
				overflow-x: auto;
			}

			.is-style-rx-code-terminal::before {
				content: "Terminal";
				display: block;
				font-size: .75rem;
				color: #9ca3af;
				margin-bottom: .65rem;
			}
		';
	}

	/**
	 * Medical article intro pattern.
	 *
	 * @return string
	 */
	private static function pattern_medical_article_intro() {
		return '
			<!-- wp:group {"className":"is-style-rx-medical-box","layout":{"type":"constrained"}} -->
			<div class="wp-block-group is-style-rx-medical-box">
				<!-- wp:heading {"level":2,"className":"is-style-rx-heading-line"} -->
				<h2 class="wp-block-heading is-style-rx-heading-line">Medical Overview</h2>
				<!-- /wp:heading -->

				<!-- wp:paragraph {"className":"is-style-rx-lead"} -->
				<p class="is-style-rx-lead">This section gives a simple, evidence-based overview of the condition, including meaning, causes, symptoms, diagnosis, treatment, and prevention.</p>
				<!-- /wp:paragraph -->

				<!-- wp:list {"className":"is-style-rx-check-list"} -->
				<ul class="is-style-rx-check-list">
					<li>Simple definition</li>
					<li>Common causes</li>
					<li>Important symptoms</li>
					<li>Useful diagnostic tests</li>
					<li>When to see a doctor</li>
				</ul>
				<!-- /wp:list -->
			</div>
			<!-- /wp:group -->
		';
	}

	/**
	 * Evidence summary box pattern.
	 *
	 * @return string
	 */
	private static function pattern_evidence_summary_box() {
		return '
			<!-- wp:group {"className":"is-style-rx-card","layout":{"type":"constrained"}} -->
			<div class="wp-block-group is-style-rx-card">
				<!-- wp:heading {"level":3} -->
				<h3 class="wp-block-heading">Evidence Summary</h3>
				<!-- /wp:heading -->

				<!-- wp:paragraph -->
				<p>This summary explains the most important clinical points in clear language. It can be used for diagnosis, treatment, prevention, and patient education sections.</p>
				<!-- /wp:paragraph -->

				<!-- wp:separator {"className":"is-style-rx-gradient-line"} -->
				<hr class="wp-block-separator has-alpha-channel-opacity is-style-rx-gradient-line"/>
				<!-- /wp:separator -->

				<!-- wp:paragraph {"className":"is-style-rx-note"} -->
				<p class="is-style-rx-note">Always connect medical decisions with clinical examination, patient history, and qualified professional judgment.</p>
				<!-- /wp:paragraph -->
			</div>
			<!-- /wp:group -->
		';
	}

	/**
	 * FAQ pattern.
	 *
	 * @return string
	 */
	private static function pattern_faq_section() {
		return '
			<!-- wp:group {"layout":{"type":"constrained"}} -->
			<div class="wp-block-group">
				<!-- wp:heading {"level":2,"className":"is-style-rx-heading-line"} -->
				<h2 class="wp-block-heading is-style-rx-heading-line">Frequently Asked Questions</h2>
				<!-- /wp:heading -->

				<!-- wp:details -->
				<details class="wp-block-details"><summary>What is this condition?</summary><p>This condition means a change in normal body structure or function. The exact meaning depends on the medical diagnosis.</p></details>
				<!-- /wp:details -->

				<!-- wp:details -->
				<details class="wp-block-details"><summary>When should I see a doctor?</summary><p>You should see a doctor if symptoms are severe, persistent, worsening, or associated with warning signs.</p></details>
				<!-- /wp:details -->

				<!-- wp:details -->
				<details class="wp-block-details"><summary>Can it be prevented?</summary><p>Some causes can be reduced by healthy lifestyle, early diagnosis, vaccination, safety habits, and regular medical follow-up.</p></details>
				<!-- /wp:details -->
			</div>
			<!-- /wp:group -->
		';
	}

	/**
	 * CTA box pattern.
	 *
	 * @return string
	 */
	private static function pattern_cta_box() {
		return '
			<!-- wp:group {"className":"is-style-rx-card","layout":{"type":"constrained"}} -->
			<div class="wp-block-group is-style-rx-card">
				<!-- wp:heading {"textAlign":"center","level":2} -->
				<h2 class="wp-block-heading has-text-align-center">Need More Health Guidance?</h2>
				<!-- /wp:heading -->

				<!-- wp:paragraph {"align":"center"} -->
				<p class="has-text-align-center">Read more evidence-based health articles written in simple language.</p>
				<!-- /wp:paragraph -->

				<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
				<div class="wp-block-buttons">
					<!-- wp:button {"className":"is-style-rx-button-primary"} -->
					<div class="wp-block-button is-style-rx-button-primary"><a class="wp-block-button__link wp-element-button">Explore Articles</a></div>
					<!-- /wp:button -->
				</div>
				<!-- /wp:buttons -->
			</div>
			<!-- /wp:group -->
		';
	}

	/**
	 * Two-column medical info pattern.
	 *
	 * @return string
	 */
	private static function pattern_two_column_medical_info() {
		return '
			<!-- wp:columns {"className":"is-style-rx-card-columns"} -->
			<div class="wp-block-columns is-style-rx-card-columns">
				<!-- wp:column -->
				<div class="wp-block-column">
					<!-- wp:group {"className":"is-style-rx-soft-card"} -->
					<div class="wp-block-group is-style-rx-soft-card">
						<!-- wp:heading {"level":3} -->
						<h3 class="wp-block-heading">Common Causes</h3>
						<!-- /wp:heading -->

						<!-- wp:list {"className":"is-style-rx-check-list"} -->
						<ul class="is-style-rx-check-list">
							<li>Genetic factors</li>
							<li>Infection or inflammation</li>
							<li>Nutritional deficiency</li>
							<li>Autoimmune disease</li>
						</ul>
						<!-- /wp:list -->
					</div>
					<!-- /wp:group -->
				</div>
				<!-- /wp:column -->

				<!-- wp:column -->
				<div class="wp-block-column">
					<!-- wp:group {"className":"is-style-rx-soft-card"} -->
					<div class="wp-block-group is-style-rx-soft-card">
						<!-- wp:heading {"level":3} -->
						<h3 class="wp-block-heading">Important Symptoms</h3>
						<!-- /wp:heading -->

						<!-- wp:list {"className":"is-style-rx-arrow-list"} -->
						<ul class="is-style-rx-arrow-list">
							<li>Pain or discomfort</li>
							<li>Fatigue or weakness</li>
							<li>Fever or swelling</li>
							<li>Changes in function</li>
						</ul>
						<!-- /wp:list -->
					</div>
					<!-- /wp:group -->
				</div>
				<!-- /wp:column -->
			</div>
			<!-- /wp:columns -->
		';
	}

	/**
	 * Add class to first HTML tag.
	 *
	 * @param string $html HTML.
	 * @param string $class Class name.
	 *
	 * @return string
	 */
	private static function add_class_to_first_tag( $html, $class ) {
		if ( empty( $html ) || empty( $class ) ) {
			return $html;
		}

		if ( false !== strpos( $html, $class ) ) {
			return $html;
		}

		if ( preg_match( '/<([a-z0-9]+)([^>]*)class="([^"]*)"/i', $html ) ) {
			return preg_replace(
				'/<([a-z0-9]+)([^>]*)class="([^"]*)"/i',
				'<$1$2class="$3 ' . esc_attr( $class ) . '"',
				$html,
				1
			);
		}

		return preg_replace(
			'/<([a-z0-9]+)([^>]*)>/i',
			'<$1$2 class="' . esc_attr( $class ) . '">',
			$html,
			1
		);
	}

	/**
	 * Add data attribute to first HTML tag.
	 *
	 * @param string $html HTML.
	 * @param string $attribute Attribute name.
	 * @param string $value Attribute value.
	 *
	 * @return string
	 */
	private static function add_data_attribute_to_first_tag( $html, $attribute, $value ) {
		if ( empty( $html ) || empty( $attribute ) ) {
			return $html;
		}

		if ( false !== strpos( $html, $attribute . '=' ) ) {
			return $html;
		}

		return preg_replace(
			'/<([a-z0-9]+)([^>]*)>/i',
			'<$1$2 ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '">',
			$html,
			1
		);
	}

	/**
	 * Add attribute to image tag if missing.
	 *
	 * @param string $html HTML.
	 * @param string $attribute Attribute.
	 * @param string $value Value.
	 *
	 * @return string
	 */
	private static function add_img_attribute( $html, $attribute, $value ) {
		if ( empty( $html ) || empty( $attribute ) ) {
			return $html;
		}

		if ( false !== strpos( $html, '<img' ) && false === strpos( $html, $attribute . '=' ) ) {
			$html = preg_replace(
				'/<img([^>]*)>/i',
				'<img$1 ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '">',
				$html,
				1
			);
		}

		return $html;
	}

	/**
	 * Get theme version.
	 *
	 * @return string
	 */
	private static function theme_version() {
		$theme = wp_get_theme();

		if ( $theme && $theme->exists() ) {
			return $theme->get( 'Version' );
		}

		return '1.0.0';
	}
}

endif;

RX_Gutenberg_Integration::init();

Optional folder files you can create later

This PHP file checks for these assets automatically:

rx-theme/assets/css/editor-style.css
rx-theme/assets/css/gutenberg-editor.css
rx-theme/assets/css/gutenberg-frontend.css
rx-theme/assets/css/gutenberg-shared.css
rx-theme/assets/js/gutenberg-editor.js
rx-theme/assets/js/gutenberg-frontend.js

Best functions.php loading line

Add this with your other includes:

if ( file_exists( get_template_directory() . '/inc/integrations/gutenberg.php' ) ) {
	require_once get_template_directory() . '/inc/integrations/gutenberg.php';
}

This file is safe because it checks WordPress functions before using them, avoids direct access, uses theme versioning, and keeps most Gutenberg functionality inside one clean class.

Leave a Reply

Your email address will not be published. Required fields are marked *