Developing Custom WordPress Blocks with Single PHP

IMPORTANT NOTE: After WordPress 7.0 Finaly we will have PHP Only Block Development Natively 🙂

I was wondering if it is possible to register custom blocks and develop it inside a single .php each

Looks like the answer is yes.

You just need to include these php files to functions.php and thats it single .php file custom blocks.

If you are new to wordpress I wouldnt recommend making blocks like this. You should just follow the official docs to create your custom blocks.

If you need a way for your edge case that you have to develop like this. Sure this is pretty cool way to develop wordpress blocks as well. Really fast too thanks to AI because everything is in single file and context is easier to handle. usualy AI tends to make mistakes on the multiple file block developing workflows.. I think we will need finetune a model for wordpress core at some point… just system prompts not cutting it.

Section Block

<?php
/**
 * Section Block - Dynamic PHP Block with Responsive Controls
 */

// Register the block
add_action('init', function() {
    // Define the attributes array separately to reuse it
    $block_attributes = [
        // Display attributes
        'display' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'flex',
                'tablet' => 'flex',
                'mobile' => 'flex'
            ]
        ],
        
        // Flex properties
        'flexDirection' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'row',
                'tablet' => 'row',
                'mobile' => 'column'
            ]
        ],
        'justifyContent' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'flex-start',
                'tablet' => 'flex-start',
                'mobile' => 'flex-start'
            ]
        ],
        'alignItems' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'stretch',
                'tablet' => 'stretch',
                'mobile' => 'stretch'
            ]
        ],
        'flexWrap' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'nowrap',
                'tablet' => 'wrap',
                'mobile' => 'wrap'
            ]
        ],
        'gap' => [
            'type' => 'object',
            'default' => [
                'desktop' => '20px',
                'tablet' => '15px',
                'mobile' => '10px'
            ]
        ],
        
        // Grid properties
        'gridTemplateColumns' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'repeat(3, 1fr)',
                'tablet' => 'repeat(2, 1fr)',
                'mobile' => '1fr'
            ]
        ],
        'gridTemplateRows' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'auto',
                'tablet' => 'auto',
                'mobile' => 'auto'
            ]
        ],
        'gridGap' => [
            'type' => 'object',
            'default' => [
                'desktop' => '20px',
                'tablet' => '15px',
                'mobile' => '10px'
            ]
        ],
        
        // Common style attributes
        'padding' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['top' => '0', 'right' => '0', 'bottom' => '0', 'left' => '0'],
                'tablet' => ['top' => '15px', 'right' => '15px', 'bottom' => '15px', 'left' => '15px'],
                'mobile' => ['top' => '10px', 'right' => '10px', 'bottom' => '10px', 'left' => '10px']
            ]
        ],
        'margin' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto'],
                'tablet' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto'],
                'mobile' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto']
            ]
        ],
        'backgroundColor' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'textColor' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'minHeight' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'maxWidth' => [
            'type' => 'object',
            'default' => [
                'desktop' => '1200px',
                'tablet' => '100%',
                'mobile' => '100%'
            ]
        ],
        
        // Border attributes
        'border' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['width' => '', 'style' => '', 'color' => '', 'radius' => ''],
                'tablet' => ['width' => '', 'style' => '', 'color' => '', 'radius' => ''],
                'mobile' => ['width' => '', 'style' => '', 'color' => '', 'radius' => '']
            ]
        ],
        
        // Additional attributes
        'className' => [
            'type' => 'string',
            'default' => ''
        ],
        'anchor' => [
            'type' => 'string',
            'default' => ''
        ]
    ];

    register_block_type('custom/section', [
        'render_callback' => 'render_section_block',
        'attributes' => $block_attributes // Use the defined attributes array
    ]);

    // Store attributes for later use in enqueue_block_editor_assets
    // This is a common pattern to pass data from PHP to JS in WordPress blocks
    global $custom_section_block_attributes;
    $custom_section_block_attributes = $block_attributes;
});

// Render callback
function render_section_block($attributes, $content) {
    $class_name = 'wp-block-custom-section';
    if (!empty($attributes['className'])) {
        $class_name .= ' ' . $attributes['className'];
    }
    
    $wrapper_attributes = [
        'class' => $class_name
    ];
    
    if (!empty($attributes['anchor'])) {
        $wrapper_attributes['id'] = $attributes['anchor'];
    }
    
    // Generate unique ID for this block instance
    $block_id = 'section-' . uniqid();
    $wrapper_attributes['data-block-id'] = $block_id;
    
    // Build wrapper attributes string
    $wrapper_attrs_string = '';
    foreach ($wrapper_attributes as $key => $value) {
        $wrapper_attrs_string .= sprintf(' %s="%s"', $key, esc_attr($value));
    }
    
    // Generate responsive CSS
    $css = section_generate_responsive_css($block_id, $attributes);
    
    // Output the block
    $output = sprintf(
        '<style>%s</style><div%s>%s</div>',
        $css,
        $wrapper_attrs_string,
        $content
    );
    
    return $output;
}

// Generate responsive CSS
function section_generate_responsive_css($block_id, $attributes) {
    $css = '';
    
    // Desktop styles (default)
    $desktop_styles = section_generate_styles_for_breakpoint($attributes, 'desktop');
    if (!empty($desktop_styles)) {
        $css .= sprintf('[data-block-id="%s"] { %s }', $block_id, $desktop_styles);
    }
    
    // Tablet styles
    $tablet_styles = section_generate_styles_for_breakpoint($attributes, 'tablet');
    if (!empty($tablet_styles)) {
        $css .= sprintf('@media (max-width: 781px) { [data-block-id="%s"] { %s } }', $block_id, $tablet_styles);
    }
    
    // Mobile styles
    $mobile_styles = section_generate_styles_for_breakpoint($attributes, 'mobile');
    if (!empty($mobile_styles)) {
        $css .= sprintf('@media (max-width: 599px) { [data-block-id="%s"] { %s } }', $block_id, $mobile_styles);
    }
    
    return $css;
}

// Generate styles for specific breakpoint
function section_generate_styles_for_breakpoint($attributes, $breakpoint) {
    $styles = [];
    
    // Display
    if (isset($attributes['display'][$breakpoint])) {
        $styles[] = sprintf('display: %s', $attributes['display'][$breakpoint]);
        
        // Flex properties
        if ($attributes['display'][$breakpoint] === 'flex') {
            if (isset($attributes['flexDirection'][$breakpoint])) {
                $styles[] = sprintf('flex-direction: %s', $attributes['flexDirection'][$breakpoint]);
            }
            if (isset($attributes['justifyContent'][$breakpoint])) {
                $styles[] = sprintf('justify-content: %s', $attributes['justifyContent'][$breakpoint]);
            }
            if (isset($attributes['alignItems'][$breakpoint])) {
                $styles[] = sprintf('align-items: %s', $attributes['alignItems'][$breakpoint]);
            }
            if (isset($attributes['flexWrap'][$breakpoint])) {
                $styles[] = sprintf('flex-wrap: %s', $attributes['flexWrap'][$breakpoint]);
            }
            if (isset($attributes['gap'][$breakpoint])) {
                $styles[] = sprintf('gap: %s', $attributes['gap'][$breakpoint]);
            }
        }
        
        // Grid properties
        if ($attributes['display'][$breakpoint] === 'grid') {
            if (isset($attributes['gridTemplateColumns'][$breakpoint])) {
                $styles[] = sprintf('grid-template-columns: %s', $attributes['gridTemplateColumns'][$breakpoint]);
            }
            if (isset($attributes['gridTemplateRows'][$breakpoint])) {
                $styles[] = sprintf('grid-template-rows: %s', $attributes['gridTemplateRows'][$breakpoint]);
            }
            if (isset($attributes['gridGap'][$breakpoint])) {
                $styles[] = sprintf('grid-gap: %s', $attributes['gridGap'][$breakpoint]);
            }
        }
    }
    
    // Padding
    if (isset($attributes['padding'][$breakpoint])) {
        $padding = $attributes['padding'][$breakpoint];
        if (is_array($padding)) {
            $styles[] = sprintf(
                'padding: %s %s %s %s',
                $padding['top'] ?? '0',
                $padding['right'] ?? '0',
                $padding['bottom'] ?? '0',
                $padding['left'] ?? '0'
            );
        }
    }
    
    // Margin
    if (isset($attributes['margin'][$breakpoint])) {
        $margin = $attributes['margin'][$breakpoint];
        if (is_array($margin)) {
            $styles[] = sprintf(
                'margin: %s %s %s %s',
                $margin['top'] ?? '0',
                $margin['right'] ?? '0',
                $margin['bottom'] ?? '0',
                $margin['left'] ?? '0'
            );
        }
    }
    
    // Colors
    if (isset($attributes['backgroundColor'][$breakpoint]) && !empty($attributes['backgroundColor'][$breakpoint])) {
        $styles[] = sprintf('background-color: %s', $attributes['backgroundColor'][$breakpoint]);
    }
    if (isset($attributes['textColor'][$breakpoint]) && !empty($attributes['textColor'][$breakpoint])) {
        $styles[] = sprintf('color: %s', $attributes['textColor'][$breakpoint]);
    }
    
    // Dimensions
    if (isset($attributes['minHeight'][$breakpoint]) && !empty($attributes['minHeight'][$breakpoint])) {
        $styles[] = sprintf('min-height: %s', $attributes['minHeight'][$breakpoint]);
    }
    if (isset($attributes['maxWidth'][$breakpoint]) && !empty($attributes['maxWidth'][$breakpoint])) {
        $styles[] = sprintf('max-width: %s', $attributes['maxWidth'][$breakpoint]);
    }
    
    // Border
    if (isset($attributes['border'][$breakpoint])) {
        $border = $attributes['border'][$breakpoint];
        if (!empty($border['width']) && !empty($border['style']) && !empty($border['color'])) {
            $styles[] = sprintf('border: %s %s %s', $border['width'], $border['style'], $border['color']);
        }
        if (!empty($border['radius'])) {
            $styles[] = sprintf('border-radius: %s', $border['radius']);
        }
    }
    
    return implode('; ', $styles);
}

// Enqueue block editor assets
add_action('enqueue_block_editor_assets', function() {
    global $custom_section_block_attributes; // Access the global variable

    // Ensure attributes are available
    $attributes_json = json_encode($custom_section_block_attributes ?: []); // Fallback to empty array if not set

    // Define a unique handle for our block's editor script
    $script_handle = 'custom-section-block-editor-script';

    // Register the script first with all necessary dependencies
    wp_register_script(
        $script_handle,
        '', // No source file, as it's an inline script
        ['wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor', 'wp-data', 'wp-viewport'],
        filemtime(__FILE__), // Use filemtime for versioning
        false // Enqueue in the header, not footer, for editor scripts
    );

    $script = "
    (function(wp) {
        // console.log('Custom Section Block: Script loaded and starting registration.'); // Debugging log
        // console.log('wp.blocks object:', wp.blocks); // Check wp.blocks object
        
        // Ensure wp.blocks and registerBlockType are available
        if (typeof wp.blocks === 'undefined' || typeof wp.blocks.registerBlockType === 'undefined') {
            console.error('Custom Section Block: wp.blocks or registerBlockType is not defined. Block will not be registered.');
            return; // Exit if dependencies are not fully loaded
        }

        const { registerBlockType } = wp.blocks;
        const { InspectorControls, InnerBlocks, useBlockProps } = wp.blockEditor;
        const { PanelBody, SelectControl, TextControl, __experimentalBoxControl: BoxControl, ColorPicker, RangeControl, ToggleControl } = wp.components;
        const { Fragment, useState, useEffect } = wp.element;
        const { select, useSelect, subscribe } = wp.data;
        const { store: viewportStore } = wp.viewport;
        
        // Get current viewport
        function getCurrentViewport() {
            const viewport = select(viewportStore);
            if (viewport.isViewportMatch('< small')) return 'mobile';
            if (viewport.isViewportMatch('< medium')) return 'tablet';
            return 'desktop';
        }
        
        registerBlockType('custom/section', {
            title: 'Section',
            icon: 'layout',
            category: 'design',
            supports: {
                html: false,
                anchor: true,
                className: true
            },
            attributes: " . $attributes_json . ", // Directly embed the JSON string
            
            edit: function(props) {
                console.log('Custom Section Block: Edit function loaded and running!'); // Debugging log
                const { attributes, setAttributes } = props;
                const [currentViewport, setCurrentViewport] = useState(getCurrentViewport());
                
                // Subscribe to viewport changes
                useEffect(() => {
                    const unsubscribe = subscribe(() => {
                        const newViewport = getCurrentViewport();
                        if (newViewport !== currentViewport) {
                            setCurrentViewport(newViewport);
                        }
                    });
                    return unsubscribe;
                }, [currentViewport]);
                
                // Helper to update responsive attribute
                const updateResponsiveAttribute = (attributeName, value) => {
                    setAttributes({
                        [attributeName]: {
                            ...attributes[attributeName],
                            [currentViewport]: value
                        }
                    });
                };
                
                // Get current value for responsive attribute
                const getResponsiveValue = (attributeName) => {
                    return attributes[attributeName]?.[currentViewport] || '';
                };
                
                const blockProps = useBlockProps({
                    style: {
                        display: getResponsiveValue('display'),
                        flexDirection: getResponsiveValue('flexDirection'),
                        justifyContent: getResponsiveValue('justifyContent'),
                        alignItems: getResponsiveValue('alignItems'),
                        flexWrap: getResponsiveValue('flexWrap'),
                        gap: getResponsiveValue('gap'),
                        gridTemplateColumns: getResponsiveValue('gridTemplateColumns'),
                        gridTemplateRows: getResponsiveValue('gridTemplateRows'),
                        gridGap: getResponsiveValue('gridGap'),
                        padding: getResponsiveValue('padding') ? Object.values(getResponsiveValue('padding')).join(' ') : undefined,
                        margin: getResponsiveValue('margin') ? Object.values(getResponsiveValue('margin')).join(' ') : undefined,
                        backgroundColor: getResponsiveValue('backgroundColor'),
                        color: getResponsiveValue('textColor'),
                        minHeight: getResponsiveValue('minHeight'),
                        maxWidth: getResponsiveValue('maxWidth'),
                    }
                });
                
                return wp.element.createElement(
                    Fragment,
                    null,
                    wp.element.createElement(
                        InspectorControls,
                        null,
                        wp.element.createElement(
                            'div',
                            { style: { padding: '16px', backgroundColor: '#f0f0f0', marginBottom: '16px' } },
                            wp.element.createElement('strong', null, 'Current Viewport: ' + currentViewport.toUpperCase())
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Layout', initialOpen: true },
                            wp.element.createElement(
                                SelectControl,
                                {
                                    label: 'Display',
                                    value: getResponsiveValue('display'),
                                    options: [
                                        { label: 'Flex', value: 'flex' },
                                        { label: 'Grid', value: 'grid' },
                                        { label: 'Block', value: 'block' }
                                    ],
                                    onChange: (value) => updateResponsiveAttribute('display', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            ),
                            getResponsiveValue('display') === 'flex' && wp.element.createElement(
                                Fragment,
                                null,
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Flex Direction',
                                        value: getResponsiveValue('flexDirection'),
                                        options: [
                                            { label: 'Row', value: 'row' },
                                            { label: 'Column', value: 'column' },
                                            { label: 'Row Reverse', value: 'row-reverse' },
                                            { label: 'Column Reverse', value: 'column-reverse' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('flexDirection', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Justify Content',
                                        value: getResponsiveValue('justifyContent'),
                                        options: [
                                            { label: 'Start', value: 'flex-start' },
                                            { label: 'Center', value: 'center' },
                                            { label: 'End', value: 'flex-end' },
                                            { label: 'Space Between', value: 'space-between' },
                                            { label: 'Space Around', value: 'space-around' },
                                            { label: 'Space Evenly', value: 'space-evenly' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('justifyContent', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Align Items',
                                        value: getResponsiveValue('alignItems'),
                                        options: [
                                            { label: 'Stretch', value: 'stretch' },
                                            { label: 'Start', value: 'flex-start' },
                                            { label: 'Center', value: 'center' },
                                            { label: 'End', value: 'flex-end' },
                                            { label: 'Baseline', value: 'baseline' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('alignItems', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Flex Wrap',
                                        value: getResponsiveValue('flexWrap'),
                                        options: [
                                            { label: 'No Wrap', value: 'nowrap' },
                                            { label: 'Wrap', value: 'wrap' },
                                            { label: 'Wrap Reverse', value: 'wrap-reverse' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('flexWrap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Gap',
                                        value: getResponsiveValue('gap'),
                                        onChange: (value) => updateResponsiveAttribute('gap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                )
                            ),
                            getResponsiveValue('display') === 'grid' && wp.element.createElement(
                                Fragment,
                                null,
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Template Columns',
                                        value: getResponsiveValue('gridTemplateColumns'),
                                        onChange: (value) => updateResponsiveAttribute('gridTemplateColumns', value),
                                        help: 'e.g., repeat(3, 1fr) or 200px 1fr 200px',
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Template Rows',
                                        value: getResponsiveValue('gridTemplateRows'),
                                        onChange: (value) => updateResponsiveAttribute('gridTemplateRows', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Gap',
                                        value: getResponsiveValue('gridGap'),
                                        onChange: (value) => updateResponsiveAttribute('gridGap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Spacing', initialOpen: false },
                            wp.element.createElement(
                                'div',
                                { style: { marginBottom: '20px' } },
                                wp.element.createElement('label', null, 'Padding'),
                                wp.element.createElement(
                                    BoxControl,
                                    {
                                        values: getResponsiveValue('padding'),
                                        onChange: (value) => updateResponsiveAttribute('padding', value)
                                    }
                                )
                            ),
                            wp.element.createElement(
                                'div',
                                null,
                                wp.element.createElement('label', null, 'Margin'),
                                wp.element.createElement(
                                    BoxControl,
                                    {
                                        values: getResponsiveValue('margin'),
                                        onChange: (value) => updateResponsiveAttribute('margin', value)
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Colors', initialOpen: false },
                            wp.element.createElement(
                                'div',
                                { style: { marginBottom: '20px' } },
                                wp.element.createElement('label', null, 'Background Color'),
                                wp.element.createElement(
                                    ColorPicker,
                                    {
                                        color: getResponsiveValue('backgroundColor'),
                                        onChangeComplete: (value) => updateResponsiveAttribute('backgroundColor', value.hex)
                                    }
                                )
                            ),
                            wp.element.createElement(
                                'div',
                                null,
                                wp.element.createElement('label', null, 'Text Color'),
                                wp.element.createElement(
                                    ColorPicker,
                                    {
                                        color: getResponsiveValue('textColor'),
                                        onChangeComplete: (value) => updateResponsiveAttribute('textColor', value.hex)
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Dimensions', initialOpen: false },
                            wp.element.createElement(
                                TextControl,
                                {
                                    label: 'Min Height',
                                    value: getResponsiveValue('minHeight'),
                                    onChange: (value) => updateResponsiveAttribute('minHeight', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            ),
                            wp.element.createElement(
                                TextControl,
                                {
                                    label: 'Max Width',
                                    value: getResponsiveValue('maxWidth'),
                                    onChange: (value) => updateResponsiveAttribute('maxWidth', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            )
                        )
                    ),
                    wp.element.createElement(
                        'div',
                        blockProps,
                        wp.element.createElement(InnerBlocks)
                    )
                );
            },
            
            save: function() {
                return wp.element.createElement(InnerBlocks.Content);
            }
        });
        console.log('Custom Section Block: registerBlockType call completed.'); // Debugging log
    })(window.wp);
    ";
    
    // Add the inline script to our custom script handle
    wp_add_inline_script($script_handle, $script);

    // Enqueue the script
    wp_enqueue_script($script_handle);
});

Container Blocks

<?php
/**
 * container Block - Dynamic PHP Block with Responsive Controls
 */

// Register the block
add_action('init', function() {
    // Define the attributes array separately to reuse it
    $block_attributes = [
        // Display attributes
        'display' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'flex',
                'tablet' => 'flex',
                'mobile' => 'flex'
            ]
        ],
        
        // Flex properties
        'flexDirection' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'row',
                'tablet' => 'row',
                'mobile' => 'column'
            ]
        ],
        'justifyContent' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'flex-start',
                'tablet' => 'flex-start',
                'mobile' => 'flex-start'
            ]
        ],
        'alignItems' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'stretch',
                'tablet' => 'stretch',
                'mobile' => 'stretch'
            ]
        ],
        'flexWrap' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'nowrap',
                'tablet' => 'wrap',
                'mobile' => 'wrap'
            ]
        ],
        'gap' => [
            'type' => 'object',
            'default' => [
                'desktop' => '20px',
                'tablet' => '15px',
                'mobile' => '10px'
            ]
        ],
        
        // Grid properties
        'gridTemplateColumns' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'repeat(3, 1fr)',
                'tablet' => 'repeat(2, 1fr)',
                'mobile' => '1fr'
            ]
        ],
        'gridTemplateRows' => [
            'type' => 'object',
            'default' => [
                'desktop' => 'auto',
                'tablet' => 'auto',
                'mobile' => 'auto'
            ]
        ],
        'gridGap' => [
            'type' => 'object',
            'default' => [
                'desktop' => '20px',
                'tablet' => '15px',
                'mobile' => '10px'
            ]
        ],
        
        // Common style attributes
        'padding' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['top' => '0', 'right' => '0', 'bottom' => '0', 'left' => '0'],
                'tablet' => ['top' => '15px', 'right' => '15px', 'bottom' => '15px', 'left' => '15px'],
                'mobile' => ['top' => '10px', 'right' => '10px', 'bottom' => '10px', 'left' => '10px']
            ]
        ],
        'margin' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto'],
                'tablet' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto'],
                'mobile' => ['top' => '0px', 'right' => 'auto', 'bottom' => '0px', 'left' => 'auto']
            ]
        ],
        'backgroundColor' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'textColor' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'minHeight' => [
            'type' => 'object',
            'default' => [
                'desktop' => '',
                'tablet' => '',
                'mobile' => ''
            ]
        ],
        'maxWidth' => [
            'type' => 'object',
            'default' => [
                'desktop' => '1200px',
                'tablet' => '100%',
                'mobile' => '100%'
            ]
        ],
        
        // Border attributes
        'border' => [
            'type' => 'object',
            'default' => [
                'desktop' => ['width' => '', 'style' => '', 'color' => '', 'radius' => ''],
                'tablet' => ['width' => '', 'style' => '', 'color' => '', 'radius' => ''],
                'mobile' => ['width' => '', 'style' => '', 'color' => '', 'radius' => '']
            ]
        ],
        
        // Additional attributes
        'className' => [
            'type' => 'string',
            'default' => ''
        ],
        'anchor' => [
            'type' => 'string',
            'default' => ''
        ]
    ];

    register_block_type('custom/container', [
        'render_callback' => 'render_container_block',
        'attributes' => $block_attributes // Use the defined attributes array
    ]);

    // Store attributes for later use in enqueue_block_editor_assets
    // This is a common pattern to pass data from PHP to JS in WordPress blocks
    global $custom_container_block_attributes;
    $custom_container_block_attributes = $block_attributes;
});

// Render callback
function render_container_block($attributes, $content) {
    $class_name = 'wp-block-custom-container';
    if (!empty($attributes['className'])) {
        $class_name .= ' ' . $attributes['className'];
    }
    
    $wrapper_attributes = [
        'class' => $class_name
    ];
    
    if (!empty($attributes['anchor'])) {
        $wrapper_attributes['id'] = $attributes['anchor'];
    }
    
    // Generate unique ID for this block instance
    $block_id = 'container-' . uniqid();
    $wrapper_attributes['data-block-id'] = $block_id;
    
    // Build wrapper attributes string
    $wrapper_attrs_string = '';
    foreach ($wrapper_attributes as $key => $value) {
        $wrapper_attrs_string .= sprintf(' %s="%s"', $key, esc_attr($value));
    }
    
    // Generate responsive CSS
    $css = container_generate_responsive_css($block_id, $attributes);
    
    // Output the block
    $output = sprintf(
        '<style>%s</style><div%s>%s</div>',
        $css,
        $wrapper_attrs_string,
        $content
    );
    
    return $output;
}

// Generate responsive CSS
function container_generate_responsive_css($block_id, $attributes) {
    $css = '';
    
    // Desktop styles (default)
    $desktop_styles = container_generate_styles_for_breakpoint($attributes, 'desktop');
    if (!empty($desktop_styles)) {
        $css .= sprintf('[data-block-id="%s"] { %s }', $block_id, $desktop_styles);
    }
    
    // Tablet styles
    $tablet_styles = container_generate_styles_for_breakpoint($attributes, 'tablet');
    if (!empty($tablet_styles)) {
        $css .= sprintf('@media (max-width: 781px) { [data-block-id="%s"] { %s } }', $block_id, $tablet_styles);
    }
    
    // Mobile styles
    $mobile_styles = container_generate_styles_for_breakpoint($attributes, 'mobile');
    if (!empty($mobile_styles)) {
        $css .= sprintf('@media (max-width: 599px) { [data-block-id="%s"] { %s } }', $block_id, $mobile_styles);
    }
    
    return $css;
}

// Generate styles for specific breakpoint
function container_generate_styles_for_breakpoint($attributes, $breakpoint) {
    $styles = [];
    
    // Display
    if (isset($attributes['display'][$breakpoint])) {
        $styles[] = sprintf('display: %s', $attributes['display'][$breakpoint]);
        
        // Flex properties
        if ($attributes['display'][$breakpoint] === 'flex') {
            if (isset($attributes['flexDirection'][$breakpoint])) {
                $styles[] = sprintf('flex-direction: %s', $attributes['flexDirection'][$breakpoint]);
            }
            if (isset($attributes['justifyContent'][$breakpoint])) {
                $styles[] = sprintf('justify-content: %s', $attributes['justifyContent'][$breakpoint]);
            }
            if (isset($attributes['alignItems'][$breakpoint])) {
                $styles[] = sprintf('align-items: %s', $attributes['alignItems'][$breakpoint]);
            }
            if (isset($attributes['flexWrap'][$breakpoint])) {
                $styles[] = sprintf('flex-wrap: %s', $attributes['flexWrap'][$breakpoint]);
            }
            if (isset($attributes['gap'][$breakpoint])) {
                $styles[] = sprintf('gap: %s', $attributes['gap'][$breakpoint]);
            }
        }
        
        // Grid properties
        if ($attributes['display'][$breakpoint] === 'grid') {
            if (isset($attributes['gridTemplateColumns'][$breakpoint])) {
                $styles[] = sprintf('grid-template-columns: %s', $attributes['gridTemplateColumns'][$breakpoint]);
            }
            if (isset($attributes['gridTemplateRows'][$breakpoint])) {
                $styles[] = sprintf('grid-template-rows: %s', $attributes['gridTemplateRows'][$breakpoint]);
            }
            if (isset($attributes['gridGap'][$breakpoint])) {
                $styles[] = sprintf('grid-gap: %s', $attributes['gridGap'][$breakpoint]);
            }
        }
    }
    
    // Padding
    if (isset($attributes['padding'][$breakpoint])) {
        $padding = $attributes['padding'][$breakpoint];
        if (is_array($padding)) {
            $styles[] = sprintf(
                'padding: %s %s %s %s',
                $padding['top'] ?? '0',
                $padding['right'] ?? '0',
                $padding['bottom'] ?? '0',
                $padding['left'] ?? '0'
            );
        }
    }
    
    // Margin
    if (isset($attributes['margin'][$breakpoint])) {
        $margin = $attributes['margin'][$breakpoint];
        if (is_array($margin)) {
            $styles[] = sprintf(
                'margin: %s %s %s %s',
                $margin['top'] ?? '0',
                $margin['right'] ?? '0',
                $margin['bottom'] ?? '0',
                $margin['left'] ?? '0'
            );
        }
    }
    
    // Colors
    if (isset($attributes['backgroundColor'][$breakpoint]) && !empty($attributes['backgroundColor'][$breakpoint])) {
        $styles[] = sprintf('background-color: %s', $attributes['backgroundColor'][$breakpoint]);
    }
    if (isset($attributes['textColor'][$breakpoint]) && !empty($attributes['textColor'][$breakpoint])) {
        $styles[] = sprintf('color: %s', $attributes['textColor'][$breakpoint]);
    }
    
    // Dimensions
    if (isset($attributes['minHeight'][$breakpoint]) && !empty($attributes['minHeight'][$breakpoint])) {
        $styles[] = sprintf('min-height: %s', $attributes['minHeight'][$breakpoint]);
    }
    if (isset($attributes['maxWidth'][$breakpoint]) && !empty($attributes['maxWidth'][$breakpoint])) {
        $styles[] = sprintf('max-width: %s', $attributes['maxWidth'][$breakpoint]);
    }
    
    // Border
    if (isset($attributes['border'][$breakpoint])) {
        $border = $attributes['border'][$breakpoint];
        if (!empty($border['width']) && !empty($border['style']) && !empty($border['color'])) {
            $styles[] = sprintf('border: %s %s %s', $border['width'], $border['style'], $border['color']);
        }
        if (!empty($border['radius'])) {
            $styles[] = sprintf('border-radius: %s', $border['radius']);
        }
    }
    
    return implode('; ', $styles);
}

// Enqueue block editor assets
add_action('enqueue_block_editor_assets', function() {
    global $custom_container_block_attributes; // Access the global variable

    // Ensure attributes are available
    $attributes_json = json_encode($custom_container_block_attributes ?: []); // Fallback to empty array if not set

    // Define a unique handle for our block's editor script
    $script_handle = 'custom-container-block-editor-script';

    // Register the script first with all necessary dependencies
    wp_register_script(
        $script_handle,
        '', // No source file, as it's an inline script
        ['wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor', 'wp-data', 'wp-viewport'],
        filemtime(__FILE__), // Use filemtime for versioning
        false // Enqueue in the header, not footer, for editor scripts
    );

    $script = "
    (function(wp) {
        // console.log('Custom container Block: Script loaded and starting registration.'); // Debugging log
        // console.log('wp.blocks object:', wp.blocks); // Check wp.blocks object
        
        // Ensure wp.blocks and registerBlockType are available
        if (typeof wp.blocks === 'undefined' || typeof wp.blocks.registerBlockType === 'undefined') {
            console.error('Custom container Block: wp.blocks or registerBlockType is not defined. Block will not be registered.');
            return; // Exit if dependencies are not fully loaded
        }

        const { registerBlockType } = wp.blocks;
        const { InspectorControls, InnerBlocks, useBlockProps } = wp.blockEditor;
        const { PanelBody, SelectControl, TextControl, __experimentalBoxControl: BoxControl, ColorPicker, RangeControl, ToggleControl } = wp.components;
        const { Fragment, useState, useEffect } = wp.element;
        const { select, useSelect, subscribe } = wp.data;
        const { store: viewportStore } = wp.viewport;
        
        // Get current viewport
        function getCurrentViewport() {
            const viewport = select(viewportStore);
            if (viewport.isViewportMatch('< small')) return 'mobile';
            if (viewport.isViewportMatch('< medium')) return 'tablet';
            return 'desktop';
        }
        
        registerBlockType('custom/container', {
            title: 'Container',
            icon: 'layout',
            category: 'design',
            supports: {
                html: false,
                anchor: true,
                className: true
            },
            attributes: " . $attributes_json . ", // Directly embed the JSON string
            
            edit: function(props) {
                console.log('Custom container Block: Edit function loaded and running!'); // Debugging log
                const { attributes, setAttributes } = props;
                const [currentViewport, setCurrentViewport] = useState(getCurrentViewport());
                
                // Subscribe to viewport changes
                useEffect(() => {
                    const unsubscribe = subscribe(() => {
                        const newViewport = getCurrentViewport();
                        if (newViewport !== currentViewport) {
                            setCurrentViewport(newViewport);
                        }
                    });
                    return unsubscribe;
                }, [currentViewport]);
                
                // Helper to update responsive attribute
                const updateResponsiveAttribute = (attributeName, value) => {
                    setAttributes({
                        [attributeName]: {
                            ...attributes[attributeName],
                            [currentViewport]: value
                        }
                    });
                };
                
                // Get current value for responsive attribute
                const getResponsiveValue = (attributeName) => {
                    return attributes[attributeName]?.[currentViewport] || '';
                };
                
                const blockProps = useBlockProps({
                    style: {
                        display: getResponsiveValue('display'),
                        flexDirection: getResponsiveValue('flexDirection'),
                        justifyContent: getResponsiveValue('justifyContent'),
                        alignItems: getResponsiveValue('alignItems'),
                        flexWrap: getResponsiveValue('flexWrap'),
                        gap: getResponsiveValue('gap'),
                        gridTemplateColumns: getResponsiveValue('gridTemplateColumns'),
                        gridTemplateRows: getResponsiveValue('gridTemplateRows'),
                        gridGap: getResponsiveValue('gridGap'),
                        padding: getResponsiveValue('padding') ? Object.values(getResponsiveValue('padding')).join(' ') : undefined,
                        margin: getResponsiveValue('margin') ? Object.values(getResponsiveValue('margin')).join(' ') : undefined,
                        backgroundColor: getResponsiveValue('backgroundColor'),
                        color: getResponsiveValue('textColor'),
                        minHeight: getResponsiveValue('minHeight'),
                        maxWidth: getResponsiveValue('maxWidth'),
                    }
                });
                
                return wp.element.createElement(
                    Fragment,
                    null,
                    wp.element.createElement(
                        InspectorControls,
                        null,
                        wp.element.createElement(
                            'div',
                            { style: { padding: '16px', backgroundColor: '#f0f0f0', marginBottom: '16px' } },
                            wp.element.createElement('strong', null, 'Current Viewport: ' + currentViewport.toUpperCase())
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Layout', initialOpen: true },
                            wp.element.createElement(
                                SelectControl,
                                {
                                    label: 'Display',
                                    value: getResponsiveValue('display'),
                                    options: [
                                        { label: 'Flex', value: 'flex' },
                                        { label: 'Grid', value: 'grid' },
                                        { label: 'Block', value: 'block' }
                                    ],
                                    onChange: (value) => updateResponsiveAttribute('display', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            ),
                            getResponsiveValue('display') === 'flex' && wp.element.createElement(
                                Fragment,
                                null,
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Flex Direction',
                                        value: getResponsiveValue('flexDirection'),
                                        options: [
                                            { label: 'Row', value: 'row' },
                                            { label: 'Column', value: 'column' },
                                            { label: 'Row Reverse', value: 'row-reverse' },
                                            { label: 'Column Reverse', value: 'column-reverse' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('flexDirection', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Justify Content',
                                        value: getResponsiveValue('justifyContent'),
                                        options: [
                                            { label: 'Start', value: 'flex-start' },
                                            { label: 'Center', value: 'center' },
                                            { label: 'End', value: 'flex-end' },
                                            { label: 'Space Between', value: 'space-between' },
                                            { label: 'Space Around', value: 'space-around' },
                                            { label: 'Space Evenly', value: 'space-evenly' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('justifyContent', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Align Items',
                                        value: getResponsiveValue('alignItems'),
                                        options: [
                                            { label: 'Stretch', value: 'stretch' },
                                            { label: 'Start', value: 'flex-start' },
                                            { label: 'Center', value: 'center' },
                                            { label: 'End', value: 'flex-end' },
                                            { label: 'Baseline', value: 'baseline' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('alignItems', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    SelectControl,
                                    {
                                        label: 'Flex Wrap',
                                        value: getResponsiveValue('flexWrap'),
                                        options: [
                                            { label: 'No Wrap', value: 'nowrap' },
                                            { label: 'Wrap', value: 'wrap' },
                                            { label: 'Wrap Reverse', value: 'wrap-reverse' }
                                        ],
                                        onChange: (value) => updateResponsiveAttribute('flexWrap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Gap',
                                        value: getResponsiveValue('gap'),
                                        onChange: (value) => updateResponsiveAttribute('gap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                )
                            ),
                            getResponsiveValue('display') === 'grid' && wp.element.createElement(
                                Fragment,
                                null,
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Template Columns',
                                        value: getResponsiveValue('gridTemplateColumns'),
                                        onChange: (value) => updateResponsiveAttribute('gridTemplateColumns', value),
                                        help: 'e.g., repeat(3, 1fr) or 200px 1fr 200px',
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Template Rows',
                                        value: getResponsiveValue('gridTemplateRows'),
                                        onChange: (value) => updateResponsiveAttribute('gridTemplateRows', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                ),
                                wp.element.createElement(
                                    TextControl,
                                    {
                                        label: 'Grid Gap',
                                        value: getResponsiveValue('gridGap'),
                                        onChange: (value) => updateResponsiveAttribute('gridGap', value),
                                        // Opt-in to new default size and no bottom margin
                                        __next40pxDefaultSize: true,
                                        __nextHasNoMarginBottom: true
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Spacing', initialOpen: false },
                            wp.element.createElement(
                                'div',
                                { style: { marginBottom: '20px' } },
                                wp.element.createElement('label', null, 'Padding'),
                                wp.element.createElement(
                                    BoxControl,
                                    {
                                        values: getResponsiveValue('padding'),
                                        onChange: (value) => updateResponsiveAttribute('padding', value)
                                    }
                                )
                            ),
                            wp.element.createElement(
                                'div',
                                null,
                                wp.element.createElement('label', null, 'Margin'),
                                wp.element.createElement(
                                    BoxControl,
                                    {
                                        values: getResponsiveValue('margin'),
                                        onChange: (value) => updateResponsiveAttribute('margin', value)
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Colors', initialOpen: false },
                            wp.element.createElement(
                                'div',
                                { style: { marginBottom: '20px' } },
                                wp.element.createElement('label', null, 'Background Color'),
                                wp.element.createElement(
                                    ColorPicker,
                                    {
                                        color: getResponsiveValue('backgroundColor'),
                                        onChangeComplete: (value) => updateResponsiveAttribute('backgroundColor', value.hex)
                                    }
                                )
                            ),
                            wp.element.createElement(
                                'div',
                                null,
                                wp.element.createElement('label', null, 'Text Color'),
                                wp.element.createElement(
                                    ColorPicker,
                                    {
                                        color: getResponsiveValue('textColor'),
                                        onChangeComplete: (value) => updateResponsiveAttribute('textColor', value.hex)
                                    }
                                )
                            )
                        ),
                        wp.element.createElement(
                            PanelBody,
                            { title: 'Dimensions', initialOpen: false },
                            wp.element.createElement(
                                TextControl,
                                {
                                    label: 'Min Height',
                                    value: getResponsiveValue('minHeight'),
                                    onChange: (value) => updateResponsiveAttribute('minHeight', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            ),
                            wp.element.createElement(
                                TextControl,
                                {
                                    label: 'Max Width',
                                    value: getResponsiveValue('maxWidth'),
                                    onChange: (value) => updateResponsiveAttribute('maxWidth', value),
                                    // Opt-in to new default size and no bottom margin
                                    __next40pxDefaultSize: true,
                                    __nextHasNoMarginBottom: true
                                }
                            )
                        )
                    ),
                    wp.element.createElement(
                        'div',
                        blockProps,
                        wp.element.createElement(InnerBlocks)
                    )
                );
            },
            
            save: function() {
                return wp.element.createElement(InnerBlocks.Content);
            }
        });
        console.log('Custom container Block: registerBlockType call completed.'); // Debugging log
    })(window.wp);
    ";
    
    // Add the inline script to our custom script handle
    wp_add_inline_script($script_handle, $script);

    // Enqueue the script
    wp_enqueue_script($script_handle);
});

Heading Block

<?php
/**
 * Custom Heading Block
 * Registers a simple heading block with typography controls.
 * This file contains all the necessary PHP and JavaScript for the block.
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Register the block type.
 */
function custom_heading_block_init() {
    // These attributes are used by the render_callback on the server.
    $block_attributes = array(
        'content'         => array(
            'type'    => 'string',
            'source'  => 'html',
            'selector'=> 'h1,h2,h3,h4,h5,h6',
        ),
        'level'           => array(
            'type'    => 'number',
            'default' => 2,
        ),
        'textAlign'       => array(
            'type'    => 'string',
            'default' => 'left',
        ),
        'fontSize'        => array(
            'type'    => 'string',
        ),
        'lineHeight'      => array(
            'type'    => 'string',
        ),
        'textColor'       => array(
            'type'    => 'string',
        ),
        'backgroundColor' => array(
            'type'    => 'string',
        ),
        'className'       => array(
            'type'    => 'string',
        ),
    );

    // Register the block.
    register_block_type( 'custom/heading', array(
        'editor_script'   => 'custom-heading-block-editor-script',
        'render_callback' => 'custom_heading_render_callback',
        'attributes'      => $block_attributes,
    ) );
}
add_action( 'init', 'custom_heading_block_init' );

/**
 * Enqueue block editor assets.
 */
function custom_heading_block_editor_assets() {
    add_action( 'admin_head', 'custom_heading_block_editor_script' );
    wp_enqueue_script(
        'custom-heading-block-editor-script',
        '', // No source file, as it's an inline script
        array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-i18n' ),
        '1.0.4' // Updated version
    );
}
add_action( 'enqueue_block_editor_assets', 'custom_heading_block_editor_assets' );

/**
 * Prints the JavaScript for the block editor.
 */
function custom_heading_block_editor_script() {
    ?>
    <script>
    (function(wp) {
        const { registerBlockType } = wp.blocks;
        const { RichText, InspectorControls, PanelColorSettings, AlignmentToolbar, BlockControls } = wp.blockEditor;
        const { PanelBody, SelectControl, TextControl } = wp.components;
        const { Fragment } = wp.element;

        registerBlockType('custom/heading', {
            title: 'Custom Heading',
            icon: 'heading',
            category: 'common',
            attributes: {
                content: {
                    type: 'string',
                    source: 'html',
                    selector: 'h1,h2,h3,h4,h5,h6',
                },
                level: {
                    type: 'number',
                    default: 2,
                },
                textAlign: {
                    type: 'string',
                    default: 'left',
                },
                fontSize: {
                    type: 'string',
                },
                lineHeight: {
                    type: 'string',
                },
                textColor: {
                    type: 'string',
                },
                backgroundColor: {
                    type: 'string',
                },
                className: {
                    type: 'string',
                },
            },
            edit: function({ attributes, setAttributes }) {
                const { content, level, textAlign, fontSize, lineHeight, textColor, backgroundColor } = attributes;

                const blockStyle = {
                    textAlign,
                    fontSize,
                    lineHeight,
                    color: textColor,
                    backgroundColor,
                };

                return wp.element.createElement(
                    Fragment,
                    null,
                    wp.element.createElement(
                        InspectorControls,
                        null,
                        wp.element.createElement(
                            PanelBody, { title: 'Heading Settings', initialOpen: true },
                            wp.element.createElement(SelectControl, {
                                label: 'Heading Level',
                                value: level,
                                options: [
                                    { label: 'H1', value: 1 }, { label: 'H2', value: 2 }, { label: 'H3', value: 3 },
                                    { label: 'H4', value: 4 }, { label: 'H5', value: 5 }, { label: 'H6', value: 6 },
                                ],
                                onChange: (newLevel) => setAttributes({ level: parseInt(newLevel) }),
                            })
                        ),
                        wp.element.createElement(
                            PanelBody, { title: 'Typography', initialOpen: false },
                            wp.element.createElement(TextControl, {
                                label: 'Font Size',
                                value: fontSize,
                                onChange: (newSize) => setAttributes({ fontSize: newSize }),
                                help: 'e.g., 24px, 2em, 1.5rem',
                            }),
                            wp.element.createElement(TextControl, {
                                label: 'Line Height',
                                value: lineHeight,
                                onChange: (newLineHeight) => setAttributes({ lineHeight: newLineHeight }),
                                help: 'e.g., 1.2, 1.5',
                            })
                        ),
                        wp.element.createElement(PanelColorSettings, {
                            title: 'Color Settings',
                            initialOpen: false,
                            colorSettings: [
                                { label: 'Text Color', onChange: (newColor) => setAttributes({ textColor: newColor }), value: textColor },
                                { label: 'Background Color', onChange: (newColor) => setAttributes({ backgroundColor: newColor }), value: backgroundColor },
                            ],
                        })
                    ),
                    wp.element.createElement(
                        BlockControls,
                        null,
                        wp.element.createElement(AlignmentToolbar, {
                            value: textAlign,
                            onChange: (newAlign) => setAttributes({ textAlign: newAlign }),
                        })
                    ),
                    wp.element.createElement(RichText, {
                        tagName: `h${level}`,
                        value: content,
                        onChange: (newContent) => setAttributes({ content: newContent }),
                        placeholder: 'Your Heading Here',
                        style: blockStyle,
                        allowedFormats: [ 'core/bold', 'core/italic', 'core/link' ]
                    })
                );
            },
            save: function() {
                return null;
            },
        });
    })(window.wp);
    </script>
    <?php
}

/**
 * Render the block dynamically on the server.
 */
function custom_heading_render_callback( $attributes, $content ) {
    $level = ( isset( $attributes['level'] ) && $attributes['level'] >= 1 && $attributes['level'] <= 6 ) ? $attributes['level'] : 2;
    $tag = 'h' . $level;
    
    $heading_content = isset( $attributes['content'] ) ? $attributes['content'] : '';

    $style = '';
    $style_props = [];
    if ( ! empty( $attributes['textAlign'] ) ) { $style_props[] = 'text-align:' . esc_attr( $attributes['textAlign'] ); }
    if ( ! empty( $attributes['fontSize'] ) ) { $style_props[] = 'font-size:' . esc_attr( $attributes['fontSize'] ); }
    if ( ! empty( $attributes['lineHeight'] ) ) { $style_props[] = 'line-height:' . esc_attr( $attributes['lineHeight'] ); }
    if ( ! empty( $attributes['textColor'] ) ) { $style_props[] = 'color:' . esc_attr( $attributes['textColor'] ); }
    if ( ! empty( $attributes['backgroundColor'] ) ) { $style_props[] = 'background-color:' . esc_attr( $attributes['backgroundColor'] ); }

    if (!empty($style_props)) {
        $style = implode(';', $style_props);
    }

    $class_name = 'wp-block-custom-heading';
    if ( ! empty( $attributes['className'] ) ) {
        $class_name .= ' ' . esc_attr( $attributes['className'] );
    }

    return sprintf(
        '<%s class="%s" style="%s">%s</%s>',
        $tag,
        esc_attr( $class_name ),
        esc_attr( $style ),
        $heading_content,
        $tag
    );
}

functions.php

// Custom Blocks
require_once SNN_PATH . '/blocks/section.php';
require_once SNN_PATH . '/blocks/container.php';
require_once SNN_PATH . '/blocks/heading.php';