Building an Advanced Separator Block with PHP-Only Block Registration

on in WordPress | Last modified on

This article is part of a series on PHP-only Gutenberg block registration. For an introduction to the feature and its background, read PHP-Only Block Registration in WordPress.


WordPress includes a core Separator block, but it is intentionally minimal — a thin <hr> with a handful of preset styles. If you want a separator that an editor can resize, recolour, and swap between striped, dotted, or shaded variants, you need to build it yourself.

This block replaces the need for a JavaScript-registered block entirely. It is registered in PHP, styled in CSS, and the three decorative variants are registered as block styles via register_block_style() — a WordPress API that requires zero JavaScript to use from PHP.


What the block does

  • Text input for line width (accepts any CSS value: 256px, 50%, 10rem)
  • Text input for line height (e.g. 10px, 1rem)
  • Number input for vertical padding (in pixels)
  • Dropdown for alignment (Left / Centre / Right)
  • Native colour pickers: Text Color = line colour (used via currentColor), Background = container background
  • Three block styles (Styles panel in the sidebar): Default, Striped, Shade, Dotted
  • Outer margin via supports.spacing
  • Wide and full alignment support

The mini-plugin

advanced-separator-block/
├── advanced-separator-block.php
└── style.css

advanced-separator-block/advanced-separator-block.php

<?php
/**
 * Plugin Name: Advanced Separator Block
 * Description: A PHP-only Advanced Separator Gutenberg block with decorative style variants.
 * Version:     1.0.0
 * Requires at least: 6.7
 * Requires Plugins: gutenberg
 */

defined( 'ABSPATH' ) || exit;

define( 'ADV_SEP_VERSION', '1.0.0' );

// ---------------------------------------------------------------------------
// Block registration
// ---------------------------------------------------------------------------

add_action( 'init', 'adv_sep_block_register' );

function adv_sep_block_register(): void {
    register_block_type(
        'my-plugin/advanced-separator',
        array(
            'title'       => 'Advanced Separator',
            'description' => 'A styled horizontal separator with configurable size, alignment, and decorative variants.',
            'category'    => 'design',
            'icon'        => 'minus',

            'attributes' => array(
                'lineWidth' => array(
                    'type'    => 'string',
                    'default' => '256px',
                    'label'   => 'Line Width',
                ),
                'lineHeight' => array(
                    'type'    => 'string',
                    'default' => '10px',
                    'label'   => 'Line Height',
                ),
                'linePadding' => array(
                    'type'    => 'integer',
                    'default' => 24,
                    'label'   => 'Line Padding (px)',
                ),
                // Sentence-case enum values are used directly as SelectControl
                // option labels. 'Left' displays as "Left" in the editor.
                'alignment' => array(
                    'type'    => 'string',
                    'enum'    => array( 'Left', 'Center', 'Right' ),
                    'default' => 'Center',
                    'label'   => 'Alignment',
                ),
            ),

            'supports' => array(
                'autoRegister' => true,
                'align'        => array( 'wide', 'full' ),
                // color.text       → line colour via currentColor in CSS.
                // color.background → container background (visible with Shade style).
                'color'   => array(
                    'text'       => true,
                    'background' => true,
                ),
                'spacing' => array( 'margin' => true ),
            ),

            'render_callback' => 'adv_sep_block_render',
        )
    );

    // Block styles appear in the Styles panel in the sidebar.
    // No JavaScript needed — register_block_style() works from PHP.
    register_block_style(
        'my-plugin/advanced-separator',
        array( 'name' => 'striped', 'label' => 'Striped' )
    );
    register_block_style(
        'my-plugin/advanced-separator',
        array( 'name' => 'shade',   'label' => 'Shade'   )
    );
    register_block_style(
        'my-plugin/advanced-separator',
        array( 'name' => 'dotted',  'label' => 'Dotted'  )
    );
}

// ---------------------------------------------------------------------------
// Render callback
// ---------------------------------------------------------------------------

function adv_sep_block_render( array $attributes, string $content, WP_Block $block ): string {
    $width   = esc_attr( $attributes['lineWidth']   ?? '256px' );
    $height  = esc_attr( $attributes['lineHeight']  ?? '10px' );
    $padding = absint( $attributes['linePadding']   ?? 24 );

    // Map the sentence-case enum value to a CSS flex alignment value.
    $justify_map = array(
        'Left'   => 'flex-start',
        'Center' => 'center',
        'Right'  => 'flex-end',
    );
    $justify = $justify_map[ $attributes['alignment'] ?? 'Center' ] ?? 'center';

    // get_block_wrapper_attributes() adds the has-{color}-color class for
    // palette colours, or inline style="color:…" for custom hex values.
    // The inner line div uses background-color: currentColor in CSS, so it
    // automatically picks up whatever colour the editor has chosen.
    $wrapper_attrs = get_block_wrapper_attributes(
        array( 'class' => 'adv-sep-block' )
    );

    return sprintf(
        '<div %1$s><div class="adv-sep-block__row" style="padding:%2$dpx 0;justify-content:%3$s"><div class="adv-sep-block__line" style="width:%4$s;height:%5$s"></div></div></div>',
        $wrapper_attrs,
        $padding,
        esc_attr( $justify ),
        $width,
        $height
    );
}

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

add_action( 'init', 'adv_sep_block_register_styles' );

function adv_sep_block_register_styles(): void {
    wp_enqueue_block_style(
        'my-plugin/advanced-separator',
        array(
            'handle' => 'adv-sep-block-style',
            'src'    => plugin_dir_url( __FILE__ ) . 'style.css',
            'path'   => plugin_dir_path( __FILE__ ) . 'style.css',
            'ver'    => ADV_SEP_VERSION,
        )
    );
}

add_action( 'enqueue_block_editor_assets', 'adv_sep_block_editor_styles' );

function adv_sep_block_editor_styles(): void {
    wp_enqueue_style(
        'adv-sep-block-style',
        plugin_dir_url( __FILE__ ) . 'style.css',
        array(),
        ADV_SEP_VERSION
    );
}

advanced-separator-block/style.css

/* Base */
.adv-sep-block__row  { display: flex; }
.adv-sep-block__line { background-color: currentColor; }

/* Striped
   Uses a repeating diagonal gradient with currentColor for the stripes.
   background-color: transparent overrides the default solid fill. */
.wp-block-my-plugin-advanced-separator.is-style-striped .adv-sep-block__line {
    background-image: linear-gradient(
        -45deg,
        transparent 22.5%,
        currentColor 22.5%,
        currentColor 27.5%,
        transparent 27.5%,
        transparent 47.5%,
        currentColor 47.5%,
        currentColor 52.5%,
        transparent 52.5%,
        transparent 72.5%,
        currentColor 72.5%,
        currentColor 77.5%,
        transparent 77.5%,
        transparent
    );
    background-color: transparent;
    background-position: 0.3rem 0;
    background-size: 1rem 1rem;
    border: none;
    image-rendering: pixelated;
}

/* Shade
   A soft radial gradient shadow — works best when the block background
   colour is set to match the section background. */
.wp-block-my-plugin-advanced-separator.is-style-shade .adv-sep-block__line {
    position: relative;
    background: radial-gradient(
        ellipse farthest-side at top center,
        rgba(0, 0, 0, 0.08),
        transparent
    );
}
.wp-block-my-plugin-advanced-separator.is-style-shade .adv-sep-block__line:before {
    content: "";
    display: block;
    position: absolute;
    top: 0; right: 0; left: 0;
    height: 3px;
    background: linear-gradient(
        to right,
        transparent,
        rgba(0, 0, 0, 0.02),
        rgba(0, 0, 0, 0.02),
        transparent
    );
}

/* Dotted
   A repeating radial-gradient produces the dot pattern. */
.wp-block-my-plugin-advanced-separator.is-style-dotted .adv-sep-block__line {
    background: radial-gradient(circle at center, currentColor 2px, transparent 0);
    background-size: 12px 12px;
    background-repeat: round;
    image-rendering: auto;
}

Things to know

The currentColor colour model

This block uses a single colour control — the native Text Color picker — to colour the line. How?

supports.color.text writes color: #xxx onto the block wrapper via get_block_wrapper_attributes(). The inner .adv-sep-block__line div has background-color: currentColor in CSS. currentColor is always the inherited color value from the nearest ancestor, so the line background automatically matches whatever colour the editor has chosen.

The three pattern styles (striped, dotted) already use currentColor in their gradients. The shade style uses hardcoded rgba(0,0,0,0.08) because it’s a shadow effect that doesn’t use the line colour.

supports.color.background colours the container — the space around the line. This is especially useful with the shade style, where the shadow should blend into the section background.

Block styles via register_block_style()

register_block_style() can be called directly from PHP. No wp_enqueue_script(), no registerBlockStyle in JavaScript. When selected in the editor, WordPress adds an is-style-{name} class to the block wrapper. The CSS targets that class:

.wp-block-my-plugin-advanced-separator.is-style-striped .adv-sep-block__line { … }

The CSS class for the wrapper is derived from the block name: my-plugin/advanced-separator becomes wp-block-my-plugin-advanced-separator.

Width and height as plain text inputs

The original JS block used UnitControl — a component that gives separate number and unit fields (like the spacing/sizing controls in the editor). autoRegister cannot generate a UnitControl from a string attribute; it generates a plain TextControl instead.

The trade-off in practice is small. Editors type 256px, 50%, or 10rem directly into the text field — the values are passed straight to the inline style. A UnitControl would be more polished, but for blocks that don’t need inline editing of their content, a text field is a perfectly acceptable input.

Why alignment uses flex-start/flex-end and not left/right

The row container uses display: flex. The CSS alignment properties for flex containers are flex-start, center, and flex-end, not left, right, and center. The render callback maps the stored enum values (Left, Center, Right) to the correct flex values — the editor stores the clean English label, the render callback translates it to the technical CSS value.


Further reading

Related Posts

Leave a Reply

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