Bricks Builder Docs / Custom Elements PHP / Bricks Controls PHP

Table of Contents: []

Create Your Own Elements

The Bricks child theme, which you can download from your Bricks account includes a simple custom element for demonstration purposes. The article below explains in more detail how to create your own elements programmatically.

Creating your own elements with Bricks follows a pattern similar to how you create WordPress widgets. You start by extending the Bricks\Element class and populate the required properties and methods for your element.

First, create a new file element-test.php in the root folder of your Bricks child theme.

Blank element class

<?php 
// element-test.php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Prefix_Element_Test extends \Bricks\Element {
  // Element properties
  public $category     = '';
  public $name         = '';
  public $icon         = '';
  public $css_selector = '';
  public $scripts      = [];
  public $nestable     = false; // true || @since 1.5

  // Methods: Builder-specific
  public function get_label() {}
  public function get_keywords() {}
  public function set_control_groups() {}
  public function set_controls() {}

  // Methods: Frontend-specific
  public function enqueue_scripts() {}
  public function render() {}
}

Let’s walk through the element builder properties and methods:

$categoryrequiredCategory name (all lowercase, no spaces). Use any of the predefined element categories (e.g. generalmedia, etc.) or assign your own category name.

When setting your own category make sure to provide a translatable category string for the builder using the filter: bricks/builder/i18n
$namerequiredUnique element identifier (all lowercase, no spaces). To avoid any conflicts with other elements please prefix your element name, e.g.: prefix-element-test.
$iconIcon font CSS class. Bricks includes the following icon fonts. Use any icon font CSS class to represent your element in the builder panel:Fontawesome 6 (i.e. “fas fa-anchor”)Ionicons 4 (i.e. “ion-md-alarm”)Themify Icons (i.e. “ti-bolt-alt”)
$css_selectorBy default all CSS control settings are applied to the element wrapper:  .bricks-element-wrapper. If you want the default CSS selector to target a child HTML element, set this selector here.
$nestableOmit for plain elements. Set to true to create a nestable element.
$scriptsAn array of JavaScript scripts that run when an element is rendered on the frontend or updated in the builder. The Counter element, for example, uses a script named “bricksCounter” (defined in frontend.min.js).
To load this script we use: public $scripts = ['bricksCounter'];
Please prefix all your scripts. E.g.: prefixElementTest
get_label()requiredReturn localised element label.
get_keywords()Array of strings that when matched during the element search display the element in the search results.
set_control_groups()By default, all element controls show ungrouped in the builder panel under the “Content” tab. Define custom control groups for your element controls by setting the following properties for each control group: title – Localized control group titletab – Set to either “content” or “style”
set_controls()requiredDefine element controls. For an overview of all available controls and their settings visit: Element Controls
enqueue_scripts()Load element-specific scripts and styles. Those are loaded only on pages where this element is used. Results in better performance. Example: wp_enqueue_script( 'prefix-element-test', get_template_directory_uri() . '/js/custom.js', ['jquery'], '1.0', true );
render()requiredRenders element HTML. Define HTML attributes via  $this->set_attribute() and output them via $this->render_attribute()
set_attribute( $key, $attribute, $value )Helper function to set HTML attributes for any HTML tag. $key serves as the unique identifier for this HTML tag. $attribute is the HTML attribute name. $value is a string or array which holds the attribute value(s).
render_attributes( $key )Helper function to render HTML attributes defined via $this->set_attribute(). $key serves as the unique identifier for this HTML tag.
render_dynamic_data_tag( $tag, $context, $args )Helper function to render dynamic data tags inside the render function using $this->render_dynamic_data_tag(...). An example of a $tag is the Bricks Builder Docs / Custom Elements PHP / Bricks Controls PHP. Using this helper function sets the correct post Id depending on the environment where the element is being rendered.
render_dynamic_data( $content )Helper function to render content (string) that could contain dynamic data tags. Use this helper function inside the render function calling $this->render_dynamic_data(...). Using this helper function sets the correct post Id depending on the environment where the element is being rendered.

Let’s populate our element properties and methods with some data:

<?php 
// element-test.php

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

class Prefix_Element_Test extends \Bricks\Element {
  // Element properties
  public $category     = 'general'; // Use predefined element category 'general'
  public $name         = 'prefix-test'; // Make sure to prefix your elements
  public $icon         = 'ti-bolt-alt'; // Themify icon font class
  public $css_selector = '.prefix-test-wrapper'; // Default CSS selector
  public $scripts      = ['prefixElementTest']; // Script(s) run when element is rendered on frontend or updated in builder

  // Return localised element label
  public function get_label() {
    return esc_html__( 'Test element', 'bricks' );
  }

  // Set builder control groups
  public function set_control_groups() {
    $this->control_groups['text'] = [ // Unique group identifier (lowercase, no spaces)
      'title' => esc_html__( 'Text', 'bricks' ), // Localized control group title
      'tab' => 'content', // Set to either "content" or "style"
    ];

    $this->control_groups['settings'] = [
      'title' => esc_html__( 'Settings', 'bricks' ),
      'tab' => 'content',
    ];
  }
 
  // Set builder controls
  public function set_controls() {
    $this->controls['content'] = [ // Unique control identifier (lowercase, no spaces)
      'tab' => 'content', // Control tab: content/style
      'group' => 'text', // Show under control group
      'label' => esc_html__( 'Content', 'bricks' ), // Control label
      'type' => 'text', // Control type 
      'default' => esc_html__( 'Content goes here ..', 'bricks' ), // Default setting
    ];
    
    $this->controls['type'] = [
      'tab' => 'content',
      'group' => 'settings',
      'label' => esc_html__( 'Type', 'bricks' ),
      'type' => 'select',
      'options' => [
        'info' => esc_html__( 'Info', 'bricks' ),
        'success' => esc_html__( 'Success', 'bricks' ),
        'warning' => esc_html__( 'Warning', 'bricks' ),
        'danger' => esc_html__( 'Danger', 'bricks' ),
        'muted' => esc_html__( 'Muted', 'bricks' ),
      ],
      'inline' => true,
      'clearable' => false,
      'pasteStyles' => false,
      'default' => 'info',
    ];
  }

  // Enqueue element styles and scripts
  public function enqueue_scripts() {
    wp_enqueue_script( 'prefix-test-script' );
  }

  // Render element HTML
  public function render() {
    // Set element attributes
    $root_classes[] = 'prefix-test-wrapper';

    if ( ! empty( $this->settings['type'] ) ) {
      $root_classes[] = "color-{$this->settings['type']}";
    }

    // Add 'class' attribute to element root tag
    $this->set_attribute( '_root', 'class', $root_classes );

    // Render element HTML
    // '_root' attribute is required (contains element ID, class, etc.)
    echo "<div {$this->render_attributes( '_root' )}>"; // Element root attributes
      if ( ! empty( $this->settings['content'] ) ) {
        echo "<div>{$this->settings['content']}</div>";
      }
    echo '</div>';
  }
}

All element settings are stored in $this->settings. To view of element settings you can print them on the screen like so: var_dump( $this->settings ); in the render() function.

Load and register your element

After creating your custom element you need to load and register your element. Open up functions.php of your Bricks child theme and copy & paste the following code:

/**
 * Register custom elements
 */
add_action( 'init', function() {
  $element_files = [
    __DIR__ . '/element-test.php',
  ];

  foreach ( $element_files as $file ) {
    \Bricks\Elements::register_element( $file );
  }
}, 11 );

The register_element method accepts 3 arguments:

  • $file (required): The full path to the custom element PHP file in the server
  • $name (optional): A string containing the name of the custom element (e.g.: prefix-element-test)
  • $element_class (optional): A string containing the class name of the element (e.g.: Prefix_Element_Test) which should derive from the Bricks element class (\Bricks\Element)

Note: Using the $name and $element_class arguments will improve the loading performance.

Nestable Elements (API)

Bricks 1.5 introduces Nestable Elements. Plus an API that allows you to programmatically define your own custom elements that can contain other elements. In exactly the structure you want.

Prior to Bricks 1.5 every element in Bricks was “flat”. Meaning even though an element contained a deep HTML structure (like the Slider, etc.) you couldn’t click on an inner part of this element to edit it directly (e.g. contents of slide 3), or change the inner structure of it to your liking via Drag & Drop, as you could do inside a layout element.

Making it often impossible to properly customise more complex elements like the Icon Box, Pricing Table, List, etc.

Complex elements such as the Accordion, Slider, and Tabs weren’t properly customisable at all.

Full Access & Control Over Individual Element Structure

Starting at version 1.5, Bricks provides three nestable elements:

  • Accordion (Nestable)
  • Slider (Nestable)
  • Tabs (Nestable)

Those elements were notoriously hard to properly customise due to their complex structure, and the limits of their “flat” element structure.

Now that you can populate every slide with the elements you want, purpose-specific slider-like elements such as the “Carousel” & “Team Members” are not really needed.

Nestable elements are going to exist alongside their flat origin elements for the foreseeable future until we have collected enough feedback & fixed any major bugs in order to fully make the switch to nestable elements.

Certain interactive nestable elements that are heavily JavaScript-driven (such as the Accordion, Slider, and Tabs) might prevent the Drag & drop from working 100% on the canvas. If you encounter this behavior you can always add & order elements by using the Structure panel too.

Nestable tabs: If you need to change the `display` property of the tab “pane”, please do so by adding another “Block” element inside the panel and setting the display setting there. The pane itself uses display: none in order to hide all non-active panes. If you change the display setting there, all tab panes will always be visible.

So you can start playing around with those new nestable elements, and slowly transition away from the old plain elements.

Over time we’ll convert more and more flat elements into nestable elements, so that you’ll be able to properly customize most elements in Bricks once their nestable equivalent becomes available.

Nestable Elements API

The rest of this article shows how to programmatically create your own nestable elements.

A good starting point to learn about the new nestable elements syntax is to inspect the Bricks source code of the following nestable element files:

  • accordion-nested.php
  • slider-nested.php
  • tabs-nested.php

Define Your Custom Element As “nestable”

First make sure to set the $nestable property of your custom element class to true.

This is required so Bricks knows to render this custom element using the nestable render function in the builder, and to enable drag & drop inside the builder for this custom element.

Nestable Element Template

You can define the structure of your custom element via the get_nestable_children function. It expects to return an array of element definitions.

This is best illustrated by having a look at the Nestable Slider elements’ get_nestable_children function:

public function get_nestable_children() {
  return [
      [
        'name'     => 'block',
        'label'    => esc_html__( 'Slide', 'bricks' ) . ' {item_index}',
        'settings' => [
          '_hidden' => [
            '_cssClasses' => 'hidden-class', // CSS class not visible in builder UI
          ],
        ],
        'children' => [
            [
              'name'     => 'heading',
              'settings' => [
                'text' => esc_html__( 'Slide', 'bricks' ) . ' {item_index}',
              ],
            ],
            [
              'name'     => 'button',
              'settings' => [
                'text'  => esc_html__( 'I am a button', 'bricks' ),
                'size'  => 'lg',
                'style' => 'primary',
              ],
            ],
          ],
      ],
  ];
}

The code above adds a “Slide” block inside the nestable slider, which then contains a “Heading” & “Button” element.

The children property, if set, accepts an array of further nested elements. Specify the settings array to populate individual elements inside your nestable element as needed.

Nestable Render Function (PHP)

The only new function you need to add to your PHP render() function is called render_children and it requires the element instance $this to be passed as the first parameter:

public function render() {
  $output = "<div {$this->render_attributes( '_root' )}>";

  // Render children elements (= individual items)
  $output .= Frontend::render_children( $this );

  $output .= '</div>';

  echo $output;
}

Nestable Render Function (Vue x-template)

To render elements inside your nestable element in your custom x-template, simply add the <bricks-element-children> component plus element props as shown in the following code snippet:

public function render_builder() {
  <script type="text/x-template" id="tmpl-bricks-element-custom-nestable">
    <component :is="tag">
      <h2>Title before nestable children</h2>
      <bricks-element-children :element="element"/>
      <p>Text node after nestable children</p>
    </component>
  </script>
}

Nestable Element Items (in panel)

If your nestable element structure is based on items on the same level (such as our Accordion above), then you can add a Repeater (see builder panel in screenshot above) by adding a repeater control with the items property set to children:

public function set_controls() {
  // Array of nestable element.children (@since 1.5)
  $this->controls['_children'] = [
    'type'          => 'repeater',
    'titleProperty' => 'label',
    'items'         => 'children',
  ];
];

—- CONTROLS

Text Control

The text control displays a text input field. You can set the following parameters:

  • spellcheck: true/false. (Default: false)
  • trigger: ‘keyup’/’enter’. (Default: keyup)
  • inlineEditing: Set to true to enable
class Prefix_Element_Text extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleText'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Text', 'bricks' ),
      'type' => 'text',
      'spellcheck' => true, // Default: false
      // 'trigger' => 'enter', // Default: 'enter'
      'inlineEditing' => true,
      'default' => 'Here goes your text ..',
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleText'] ) ) {
      echo $this->settings['exampleText'];
    } else {
      esc_html_e( 'No text provided.', 'bricks' );
    }
  }
}

Border Control

The border control lets you set the following border properties: 

  • Border width (top/right/bottom/left)
  • Background style (top/right/bottom/left)
  • Background color (none/solid/double/dotted/dashed)
  • Border radius (top/right/bottom/left)

The example below illustrates how to apply a border via the css parameter and how to set border defaults.

class Builder_Element_Prefix_Test extends \Bricks\Element {
  public function set_controls() {
    $this->controls['titleBorder'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Title border', 'bricks' ),
      'type' => 'border',
      'css' => [
        [
          'property' => 'border',
          'selector' => '.prefix-test-title',
        ],
      ],
      'inline' => true,
      'small' => true,
      'default' => [
        'width' => [
          'top' => 1,
          'right' => 0,
          'bottom' => 0,
          'left' => 0,
        ],
        'style' => 'solid',
        'color' => [
          'hex' => '#ffff00',
        ],
        'radius' => [
          'top' => 1,
          'right' => 1,
          'bottom' => 1,
          'left' => 1,
        ],
      ],


    ];
  }
}

Background Control

The background control lets you set the following background properties:

  • Background color
  • Background image
  • Background video (requires bricksBackgroundVideoInit script. See code example below)

There are various settings for the background image and video. You can exclude color/image/video settings via the exclude parameter.

As the background control serves most likely as a CSS setting the following example shows you how to set the css parameter to apply it to the elements’ .prefix-test-wrapper HTML.

Adding a background video requires you to load the bricksBackgroundVideoInit script and use the BricksFrontend::get_element_background_video_wrapper() method to render it.

When you just want to set a background color better use the color control. The background control is handier when using a background image or video on top of the color.

class Prefix_Element_Background extends \Bricks\Element {
  // Required for background video
  public $scripts = ['bricksBackgroundVideoInit'];

  // Set builder controls
  public function set_controls() {
    $this->controls['exampleBackground'] = [ // Setting key
      'tab' => 'content',
      'label' => esc_html__( 'Background', 'bricks' ),
      'type' => 'background',
      'css' => [
        [
          'property' => 'background',
          'selector' => '.prefix-background-wrapper',
        ],
      ],
      'exclude' => [
        // 'color',
        // 'image',
        // 'parallax',
        // 'attachment',
        // 'position',
        // 'positionX',
        // 'positionY',
        // 'repeat',
        // 'size',
        // 'custom',
        // 'videoUrl',
        // 'videoScale',
      ],
      'inline' => true,
      'small' => true,
      'default' => [
        'color' => [
          'rgb' => 'rgba(255, 255, 255, .5)',
          'hex' => '#ffffff',
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    echo '<div class="prefix-background-wrapper">';

    // Background video
    echo BricksFrontend::get_element_background_video_wrapper( 
      ['settings' => $settings], 
      'exampleBackground' // Setting key
    );

    echo get_bloginfo( 'name' );

    echo '</div>';
  }
}

Icon Control

The icon control lets you select and output icons from the following icon font libraries:

The user can also select individually uploaded SVG files if you’ve enabled “SVG Uploads” under “Bricks > Settings” in your WordPress dashboard.

class Prefix_Element_Icon extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleIcon'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Icon', 'bricks' ),
      'type' => 'icon',
      'default' => [
        'library' => 'themify', // fontawesome/ionicons/themify
        'icon' => 'ti-star',    // Example: Themify icon class
      ],
      'css' => [
        [
          'selector' => '.icon-svg', // Use to target SVG file
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    // Set icon 'class' attribute
    if ( isset( $this->settings['exampleIcon'] ) ) {
      Helpers::render_control_icon( $settings['exampleIcon'], ['test-class', 'test-class-2'] );
    }
  }
}

Checkbox Control

The checkbox control is a simple on/off switch. If enabled it outputs a boolean value of true. Disabled it returns false. You can use it to conditionally show/hide other content settings as we illustrate in the following code example:

class Prefix_Element_Checkbox extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleCheckbox'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Show site title', 'bricks' ),
      'type' => 'checkbox',
      'inline' => true,
      'small' => true,
      'default' => true, // Default: false
    ];
  }

  // Render element HTML
  public function render() {
    // Show site title if setting checkbox 'exampleCheckbox' is checked
    if ( isset( $this->settings['exampleCheckbox'] ) ) {
      echo get_bloginfo( 'name' );
    }
  }
}

Info Control

The info control does not affect the HTML or CSS on the frontend. It serves as a builder-only helper controls to provide additional information.

Example below: the Alert element displays an info control when the Type is set to Custom.

class Prefix_Element_Info extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['type'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Type', 'bricks' ),
      'type' => 'select',
      'options' => [
        'info' => esc_html__( 'Info', 'bricks' ),
        'success' => esc_html__( 'Success', 'bricks' ),
        'warning' => esc_html__( 'Warning', 'bricks' ),
        'danger' => esc_html__( 'Danger', 'bricks' ),
        'muted' => esc_html__( 'Muted', 'bricks' ),
        'custom' => esc_html__( 'Custom', 'bricks' ),
      ],
      'inline' => true,
      'clearable' => false,
      'pasteStyles' => false,
      'default' => 'info',
    ];

    $this->controls['typeInfo'] = [
      'tab' => 'content',
      'content' => esc_html__( 'Customize alert in STYLE tab.', 'bricks' ),
      'type' => 'info',
      'required' => ['type', '=', 'custom'], // Show info control if 'type' = 'custom'
    ];
  }
}

Editor Control

The editor control provides the default WordPress editor. To directly edit content in the builder preview set the inlineEditing properties. See the code example below:

class Prefix_Element_Editor extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleEditor'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Text editor', 'bricks' ),
      'type' => 'editor',
      'inlineEditing' => [
        'selector' => '.text-editor', // Mount inline editor to this CSS selector
        'toolbar' => true, // Enable/disable inline editing toolbar
      ],
      'default' => esc_html__( 'Here goes the content ..', 'bricks' ),
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleEditor'] ) ) {
      echo '<div class="text-editor">' . $this->settings['exampleEditor'] . '</div>';
    }
  }
}

Dimensions Control

The dimensions control is perfect for adding multi-directional CSS properties such as margin and padding (top/right/bottom/left). You can set the directions to anything you want via the directions property.

class Prefix_Element_Dimensions extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleDimensions'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Title padding', 'bricks' ),
      'type' => 'dimensions',
      'css' => [
        [
          'property' => 'padding',
          'selector' => '.prefix-element-dimensions-title',
        ]
      ],
      'default' => [
        'top' => '30px',
        'right' => 0,
        'bottom' => '10em',
        'left' => 0,
      ],
      // 'unitless' => false, // false by default
      // Custom directions
      // 'directions' => [
        // 'offsetX' => esc_html__( 'Offset X', 'bricks' ),
        // 'offsetY' => esc_html__( 'Offset Y', 'bricks' ),
        // 'spread'  => esc_html__( 'Spread', 'bricks' ),
        // 'blur'    => esc_html__( 'Offset Y', 'bricks' ),
      // ],
    ];
  }

  // Render element HTML
  public function render() {
    echo '<h5 class="prefix-element-dimensions-title">' . get_bloginfo( 'name' ) . '</h5>';
  }
}

Code Control

The code control embeds a code editor utilizing the amazing CodeMirror library. Users for which you’ve enabled “Code Execution” in the Bricks settings, will be able to execute PHP, HTML, CSS, and JavaScript.

class Prefix_Element_Code extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleHtml'] = [
      'tab' => 'content',
      'label' => esc_html__( 'HTML', 'bricks' ),
      'type' => 'code',
      'mode' => 'php',
      'default' => '<h4>Example H4 HTML title</h4>',
    ];
  }

  // Render element HTML
  public function render() {
    echo isset( $this->settings['exampleHtml'] ) ? $this->settings['exampleHtml'] : esc_html__( 'No HTML provided.', 'bricks' );
  }
}

You don’t need to define your own element CSS and JS controls. Those are already available when editing the element under the Style tab “CSS” control group.

Link Control

The link control give you the choice of different link types:

  • Internal post/page
  • External URL
  • Popup (image, video)
class Prefix_Element_Link extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleLink'] = [
      'tab'         => 'content',
      'label'       => esc_html__( 'Link', 'bricks' ),
      'type'        => 'link',
      'pasteStyles' => false,
      'placeholder' => esc_html__( 'http://yoursite.com', 'bricks' ),
      // 'exclude'     => [
      //  'rel',
      //  'newTab',
      // ],
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleLink'] ) ) {
      // Set link attributes by passing attribute key and link settings
      $this->set_link_attributes( 'a', $this->settings['exampleLink'] );

      echo '<a ' . $this->render_attributes( 'a' ) . '>' . get_bloginfo( 'name' ) . '</a>';
    } else {
      esc_html_e( 'No link provided.', 'bricks' );
    }
  }
}

Number Control

The number control represents a simple number input field. It has the following custom properties:

  • units (optional: boolean or array)
  • unit (string: pxemrem etc.)
  • min (number)
  • step (Default: 1) (Custom: ‘0.1’ etc.)

Use it to render a number to the page or set the css control property to target a specific CSS style.

class Prefix_Element_Number extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleNumber'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Number', 'bricks' ),
      'type' => 'number',
      'min' => 0,
      'step' => '0.1', // Default: 1
      'inline' => true,
      'default' => 123,
    ];

    $this->controls['examplePadding'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Padding in px', 'bricks' ),
      'type' => 'number',
      'unit' => 'px',
      'inline' => true,
      'css' => [
        [
          'property' => 'padding',
        ],
      ],
      'default' => 33,
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleNumber'] ) ) {
      echo esc_html__( 'Number: ', 'bricks' ) . $this->settings['exampleNumber'];
    } else {
      esc_html_e( 'No number provided.', 'bricks' );
    }
  }
}

Textarea Control

The textarea control displays a textarea input field. You can set the following parameters:

  • rows (number. Default: 5)
  • readonly (true/false. Default: false)
  • spellcheck (true/false. Default: false)
  • inlineEditing (true to enable)
class Prefix_Element_Textarea extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleTextarea'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Textarea', 'bricks' ),
      'type' => 'textarea',
      // 'readonly' => true, // Default: false
      'rows' => 10, // Default: 5
      'spellcheck' => true, // Default: false
      'inlineEditing' => true,
      'default' => 'Here goes your content ..',
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleTextarea'] ) ) {
      echo $this->settings['exampleTextarea'];
    } else {
      esc_html_e( 'No text provided.', 'bricks' );
    }
  }
}

Audio Control

The audio control lets you select an audio file from the media library. It also gives you various options to show/hide artist and title, choose between a light/dark theme, autoplay the audio file, etc. It has no custom control parameters.

class Prefix_Element_Audio extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['file'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Audio file', 'bricks' ),
      'type' => 'audio',
    ];
  }

  // Render element HTML
  public function render() {
    $settings = $this->settings;

    if ( isset( $settings['file']['url'] ) ) {
      echo wp_audio_shortcode( [
        'src'      => $settings['file']['url'],
        'loop'     => isset( $settings['loop'] ) ? $settings['loop'] : false, 
        'autoplay' => isset( $settings['autoplay'] ) ? $settings['autoplay'] : false, 
        'preload'  => isset( $settings['preload'] ) ? $settings['preload'] : 'none', 
      ] );
    }
  }
}

Box Shadow Control

The box-shadow control is a CSS control and you can set the following properties:

  • Offset X
  • Offset Y
  • Spread
  • Blur
  • Color
  • Inset
class Prefix_Element_Box_Shadow extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleBoxShadow'] = [
      'tab' => 'content',
      'label' => esc_html__( 'BoxShadow', 'bricks' ),
      'type' => 'box-shadow',
      'css' => [
        [
          'property' => 'box-shadow',
          'selector' => '.prefix-box-shadow-wrapper',
        ],
      ],
      'inline' => true,
      'small' => true,
      'default' => [
        'values' => [
          'offsetX' => 0,
          'offsetY' => 0,
          'blur' => 2,
          'spread' => 0,
        ],
        'color' => [
          'rgb' => 'rgba(0, 0, 0, .1)',
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    echo '<div class="prefix-box-shadow-wrapper">';
    echo get_bloginfo( 'name' );
    echo '</div>';
  }
}

Image Control

The image control lets you select a single image from your media library. Once an image has been selected you can choose the image size.

You can either use the returned image id and size to render an image on your page or as a background-image via the CSS control property. See the code example below.

TIP: Select the smallest possible image size in which the image still looks crisp. This helps to reduce the loading time of your website and is great for SEO, as loading times are an important ranking factor for search engines.

class Prefix_Element_Image extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleImage'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Image', 'bricks' ),
      'type' => 'image',
      // Use the selected image as a background image
      // 'css' => [
      //   [
      //     'property' => 'background-image',
      //     'selector' => '.bricks-video-overlay-image',
      //   ],
      // ],
    ];
  }

  // Render element HTML
  public function render() {
    // Dump 'exampleImage' settings on the screen
    // var_dump( $this->settings['exampleImage'] );    

    if ( isset( $this->settings['exampleImage'] ) ) {
      // Render <img> tag by prodiving image 'id' and 'size'
      // 
      echo wp_get_attachment_image(
        $this->settings['exampleImage']['id'],
        $this->settings['exampleImage']['size'],
        false,
        [] // Image attributes
      );
    } else {
      esc_html_e( 'No image selected.', 'bricks' );
    }
  }
}

Datepicker Control

The datepicker control provides a great interface for selecting a specific date and time and outputting it in the format of your choice.

The Datepicker control leverages the Flatpickr library to offer a robust date selection interface. Since Bricks 1.9.8 an options property has been added which allows for further customization.

Properties:

Property: options (since 1.9.8)

  • Type: array (associative)
  • Description: Enables customization of the datepicker by passing an associative array of options defined in the Flatpickr library.
  • Default values:
    • enableTime: Defaults to true unless explicitly set through a passed property.
    • altInput: Defaults to true unless specified otherwise through a passed property.

Example usage:

$this->controls['date'] = [
  'tab' => 'content',
  'label' => esc_html__('Date', 'bricks'),
  'type' => 'datepicker',
  'options' => [
    'enableTime' => true,  // Enables time selection.
    'time_24hr' => true,   // Displays time picker in 24-hour mode.
    'noCalendar' => true   // Hides the calendar day selection.
  ]
];

In this example, the options array is configured to create a time picker that operates in 24-hour format without showing a calendar for day selection. The enableTime option is set to true to ensure time can be selected, time_24hr is enabled for 24-hour time format, and noCalendar is set to true to hide the calendar component. Adjust the options array as needed to customize the datepicker to meet different requirements.

For a full list of customizable options available in Flatpickr, please refer to the Flatpickr Options documentation.

Property: enableTime

  • Type: boolean
  • Description: Determines whether time selection is enabled. Overridden if any settings are passed in the options property.
  • Default: true

Property: altInput

  • Type: boolean
  • Description: Enables an alternative, more user-friendly input style. Overridden if any settings are passed in the options property.
  • Default: true

Example: Countdown element

// Example: Countdown element
class Prefix_Element_Countdown extends \Bricks\Element {
  public $category = 'general';
  public $name     = 'countdown';
  public $icon     = 'ti-timer';
  public $scripts  = ['bricksCountdown'];

  public function get_label() {
    return esc_html__( 'Countdown', 'bricks' );
  }

  public function set_controls() {
    $this->controls['date'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Date', 'bricks' ),
      'type' => 'datepicker',
      'options' => ['enableTime' => true, 'altInput' => true],
      'default' => '2019-01-01 12:00',
    ];

    $this->controls['format'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Format', 'bricks' ),
      'type' => 'text',
      'default' => '%D days %H hours %M minutes %S seconds.',
      'description' => sprintf(
        '%s <a target="_blank" href="http://hilios.github.io/jQuery.countdown/documentation.html#directives">%s</a>.',
        esc_html__( 'For formatting options see', 'bricks' ),
        esc_html__( 'directives', 'bricks' )
      ),
    ];
  }

  public function render() {
    $this->set_attribute( 'wrapper', 'class', 'countdown-wrapper' );

    $countdown_options = [
      'date' => isset( $this->settings['date'] ) ? $this->settings['date'] : '',
      'format' => isset( $this->settings['format'] ) ? $this->settings['format'] : '',
    ];

    $this->set_attribute( 'wrapper', 'data-bricks-countdown-options', wp_json_encode( $countdown_options ) );

    // Render
    if ( ! isset( $this->settings['date'] ) || ! isset( $this->settings['format'] ) ) {
      return $this->render_element_placeholder( [
        'icon-class' => 'ti-timer',
        'text'       => esc_html__( 'No date/format set.', 'bricks' ),
      ] );
    } else {
      echo '<div ' . $this->render_attributes( 'wrapper' ) . '></div>';
    }
  }
}

Image Gallery Control

The image gallery control lets you select multiple images from your media library. Once images have been selected you can choose the image size.

Your selected images are stored in an array, which you have to loop through (see code example below). Use the id and size  of each image to render it on your page.

Tip #1: Hold down the SHIFT key in order to select multiple image in your media library.

Tip #2: Select the smallest possible image size in which the image still looks crisp. This helps to reduce the loading time of your website and is great for SEO, as loading times are an important ranking factor for search engines.

class Prefix_Element_Image extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleImageGallery'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Image gallery', 'bricks' ),
      'type' => 'image-gallery',
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleImageGallery'] ) ) {
      foreach( $this->settings['exampleImageGallery'] as $index => $image ) {
        echo wp_get_attachment_image(
          $image['id'],
          $image['size'],
          false,
          ['class' => 'css-filter']
        );
      }
    } else {
      esc_html_e( 'No image(s) selected.', 'bricks' );
    }
  }
}

Color Control

The color control is a custom-built color picker that you won’t find anywhere else.

It lets you pick and adjust colors in hexrgba and hsl format. It also includes a global color palette to save any color for later reuse anywhere else on your site.

Define your own default color palette with the bricks/builder/color_palette filter.

You can set the CSS property to color or background-color as illustrated in the example below.

class Prefix_Element_Color extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    // Text color applied to '.prefix-element-test-title'
    $this->controls['exampleColor'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Title color', 'bricks' ),
      'type' => 'color',
      'inline' => true,
      'css' => [
        [
          'property' => 'color',
          'selector' => '.prefix-element-test-title',
        ]
      ],
      'default' => [
        'hex' => '#3ce77b',
        'rgb' => 'rgba(60, 231, 123, 0.9)',
      ],
    ];

    // Background color applied to '.prefix-element-test-content'
    $this->controls['exampleBackgroundColor'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Background color', 'bricks' ),
      'type' => 'color',
      'inline' => true,
      'css' => [
        [
          'property' => 'background-color',
          'selector' => '.prefix-element-test-content',
        ]
      ],
      'default' => [
        'hex' => '#1ebea5',
        'rgb' => 'rgba(30, 190, 165, 0.8)',
      ],
    ];
  }

  // Render element HTML
  public function render() {
    echo '<h5 class="prefix-element-test-title">' . get_bloginfo( 'name' ) . '</h5>';
    echo '<p class="prefix-element-test-content">Just some bogus text ..</p>';
  }
}

Color palette filter

Add the following PHP code into the funcions.php file of your child theme to add your own colors to the default color palette (option #1) or replace all default colors with your own choice of colors (option #2).

// functions.php
add_filter( 'bricks/builder/color_palette', function( $colors ) {
  // Option #1: Add individual color
  $colors[] = [
    'hex' => '#3ce77b',
    'rgb' => 'rgba(60, 231, 123, 0.56)',
  ];

  // Option #2: Override entire color palette
  $colors = [
    ['hex' => '#3ce77b'],
    ['hex' => '#f1faee'],
    ['hex' => '#a8dadc'],
    ['hex' => '#457b9d'],
    ['hex' => '#1d3557'],
  ];

  return $colors;
} );

If you have saved any custom colors with the builder you need to reset your global settings in order for your new default colors to take effect.

Typography Control

The typography control provides the following CSS properties:

  • color
  • font-size
  • text-align
  • text-transform
  • font-family
  • font-weight
  • font-style
  • line-height
  • letter-spacing
  • text-shadow
  • text-decoration

Use the exclude parameter to hide specific typography properties. Set popup to false to show control inline.

class Prefix_Element_Typography extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleTypography'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Typography', 'bricks' ),
      'type' => 'typography',
      'css' => [
        [
          'property' => 'typography',
          'selector' => '.prefix-typography',
        ],
      ],
      'inline' => true,
      // 'exclude' => [
      //   'font-family',
      //   'font-weight',
      //   'text-align',
      //   'text-transform',
      //   'font-size',
      //   'line-height',
      //   'letter-spacing',
      //   'color',
      //   'text-shadow',
      // ],
      // 'popup' => false, // Default: true
    ];
  }

  // Render element HTML
  public function render() {
    echo '<h3 class="prefix-typography">' . get_bloginfo( 'name' ) . '</h3>';
  }
}

Repeater Control

The repeater control lets you create repeatable fields. Fields can be cloned, deleted, and sorted via Drag & Drop. Use the fields argument to set up the field controls.

class Prefix_Element_Posts extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleRepeater'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Repeater', 'bricks' ),
      'type' => 'repeater',
      'titleProperty' => 'title', // Default 'title'
      'default' => [
        [
          'title' => 'Design',
          'description' => 'Here goes the description for repeater item.',
        ],
        [
          'title' => 'Code',
          'description' => 'Here goes the description for repeater item.',
        ],
        [
          'title' => 'Launch',
          'description' => 'Here goes the description for repeater item.',
        ],
      ],
      'placeholder' => esc_html__( 'Title placeholder', 'bricks' ),
      'fields' => [
        'title' => [
          'label' => esc_html__( 'Title', 'bricks' ),
          'type' => 'text',
        ],
        'description' => [
          'label' => esc_html__( 'Description', 'bricks' ),
          'type' => 'textarea',
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    $items = $this->settings['exampleRepeater'];

    if ( count( $items ) ) {
      foreach ( $items as $item ) {
        echo '<h4>' . $item['title'] . '</h4>';
        echo '<p>' . $item['description'] . '</p>';
      }
    } else {
      esc_html_e( 'No items defined.', 'bricks' );
    }
  }
}

Filters Control

The filters control offers the following CSS filtersblurbrightnesscontrasthueinvertopacitysaturationsepia.

class Prefix_Element_Filters extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleFilters'] = [
      'tab' => 'content',
      'label' => esc_html__( 'CSS filters', 'bricks' ),
      'type' => 'filters',
      'inline' => true,
      'css' => [
        [
          'property' => 'filter',
          'selector' => '.css-filter',
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    echo '<div class="css-filter">' . echo get_bloginfo( 'name' ); . '</div>';
  }
}

SVG Control

The SVG control lets you select an SVG (Scalable Vector Graphic) file from the media library. The selected SVG returns an array with the following keys:

  • id (media library item ID)
  • filename
  • url

We recommend rendering the SVG inline as shown in the code example below. This way you can easily customize it via CSS.

class Prefix_Element_Svg extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleSvg'] = [
      'tab' => 'content',
      'type' => 'svg',
    ];
  }

  // Render element HTML
  public function render() {
    if ( isset( $this->settings['exampleSvg']['url'] ) ) {
      echo file_get_contents( esc_url( $this->settings['exampleSvg']['url'] ) );
    } else {
      esc_html_e( 'No SVG selected.', 'bricks' );
    }
  }
}

Text Shadow Control

The text-shadow control displays a popup that lets you set the CSS text-shadow of a specified HTML text element.

class Prefix_Element_Textarea extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleTextShadow'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Text Shadow', 'bricks' ),
      'type' => 'text-shadow',
      'css' => [
        [
          'property' => 'text-shadow',
          'selector' => '.prefix-text',
        ],
      ],
      'inline' => true,
    ];
  }

  // Render element HTML
  public function render() {
    echo '<h3 class="prefix-text">' . get_bloginfo( 'name' ) . '</h3>';
  }
}

Slider Control

The slider control shows a draggable range input field. Default units are pxem and rem. You can set the following control parameters:

  • units (array with custom units and minmaxstep attributes)
  • unitless (set to false for plain number)
class Prefix_Element_Slider extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleSliderFontSize'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Font size', 'bricks' ),
      'type' => 'slider',
      'css' => [
        [
          'property' => 'font-size',
        ],
      ],
      'units' => [
        'px' => [
          'min' => 1,
          'max' => 50,
          'step' => 1,
        ],
        'em' => [
          'min' => 1,
          'max' => 20,
          'step' => 0.1,
        ],
      ],
      'default' => '30px',
      'description' => esc_html__( 'Slider adjusts font size via CSS.', 'bricks' ),
    ];
  }

  // Render element HTML
  public function render() {
    echo '<h3>' . get_bloginfo( 'name' ) . '</h3>';
  }
}

Select Control

The select control lets you select an option from a dropdown. It can be used to render content or CSS styling. Use the options array to populate the dropdown with your own options. The option key should be all lowercase, with no spaces.





class Prefix_Element_Posts extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    // Example content 
    $this->controls['exampleSelectTitleTag'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Title tag', 'bricks' ),
      'type' => 'select',
      'options' => [
        'h1' => 'H1',
        'h2' => 'H2',
        'h3' => 'H3',
        'h4' => 'H4',
        'h5' => 'H5',
        'h6' => 'H6',
      ],
      'inline' => true,
      'placeholder' => esc_html__( 'Select tag', 'bricks' ),
      'multiple' => true, 
      'searchable' => true,
      'clearable' => true,
      'default' => 'h3',
    ];

    // Example CSS
    $this->controls['exampleSelectTextAlign'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Text align', 'bricks' ),
      'type' => 'select',
      'options' => [
        'right' => esc_html__( 'Right', 'bricks' ),
        'center' => esc_html__( 'Center', 'bricks' ),
        'left' => esc_html__( 'Left', 'bricks' ),
      ],
      'inline' => true,
      'css' => [
        [
          'property' => 'text-align',
          'selector' => '.prefix-title',
        ],
      ],
      'placeholder' => esc_html__( 'Select', 'bricks' ),
      'default' => 'center', // Option key
    ];
  }

  // Render element HTML
  public function render() {
    $title_tag = isset( $this->settings['exampleSelectTitleTag'] ) ? $this->settings['exampleSelectTitleTag'] : 'h5';
    echo '<' . $title_tag . ' class="prefix-title">' . get_bloginfo( 'name' ) . '</' . $title_tag . '>';
  }
}

Element Controls

Element controls allow the user to change the content and appearance of an element. You can define the controls of an element with the set_controls() method in your element PHP class.

Example element class with control parameters for control testColor:

class Prefix_Element_Test extends \Bricks\Element {
  public function set_controls() {
    $this->controls['testColor'] = [
      'tab' => 'content',
      'group' => 'settings',
      'label' => esc_html__( 'Text color', 'bricks' ),
      'type' => 'color',
      'inline' => true,
      'small' => true,
      'css' => [
        [
          'property' => 'color',
          'selector' => '.content',
          'important' => true, // Optional
        ],
      ],
      'default' => [
        'rgb' => 'rgba(158, 158, 158, .8)',
        'hex' => '#9e9e9e',
      ],
      'pasteStyles' => false,
      'description' => esc_html__( 'Define the content color.', 'bricks' ),
      'required' => ['showText', '!=', ''],
    ];
  }
}

The following control parameters are available for all control types. To dive deeper into the arguments of a specific control type select the control from the list at the bottom.

Universal control arguments

NameTypeDefaultDescription
tabstringcontentTab under which to show the control. Accepts: content or style.
groupstring Group under which to show the control. By default a control shows ungrouped under the content tab.
labelstring Localized control label. E.g.: esc_html__( 'Color', 'bricks' ),
typestring Set the control type (see the list below for a list of all available control types).
inlineboolfalseSet to true to show control label and input on the same line.
smallboolfalseSet to true to show a control input of 60px width. By default inline label and input have equal widths of 50%.
cssarray Array with CSS rule definitions. Each CSS rule is a separate array and requires a property and selector parameter.
defaultstring/array Default control value. Either a string or an array (depending on the control type, see control list below for specific control default)
pasteStylesbooltrueSet to true excludes setting from being pasted via the builders’ custom right click “Paste Styles”. Recommended for all controls that output HTML content instead of CSS.
descriptionstring Optional description for controls that need additional explanation or link to a resource.
requiredarray Show control in relation to the setting of another control. Parameter #1: control ID
Parameter #2: comparison operator:  =!=>=<=
Parameter #3: setting valueExample: 'required' => ['layout', '=', ['list', 'grid']],
Required condition: Show this control if setting value of control layout equals = either list or grid.

Controls Types

Control TypeOutput (Content/CSS)
applyNone
align-itemsCSS
audioContent
backgroundCSS
borderCSS
box-shadowCSS
checkboxConditional
codeContent
colorCSS
datepickerContent
dimensionsCSS
directionCSS
editorContent
filtersCSS
gradientCSS
iconContent
imageContent/CSS
image-galleryContent
infoBuilder panel only
justify-contentCSS
linkContent
numberContent/CSS
postsContent
repeaterContent
selectContent/CSS
sliderContent
svgContent
textContent
textareaContent
text-alignCSS
text-decorationCSS
text-shadowCSS
text-transformCSS
typographyCSS

Query Control

The query control lets you set query arguments to retrieve items of any post type. Use the returned value to set up a custom WP_Query to render the matching posts in any way you want.





class Prefix_Element_Posts extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleQueryArgs'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Posts', 'bricks' ),
      'type' => 'query',
      // Default required for query to populate
      'default' => [
        'post_type' => 'post',
      ],
    ];
  }

  // Render element HTML
  public function render() {
    $query_args = $this->settings['exampleQueryArgs'];
    $posts_query = new WP_Query( $query_args );

    // Standard WordPress loop
    if ( $posts_query->have_posts() ) :
      while ( $posts_query->have_posts() ) : $posts_query->the_post();
        // Render post title and thumbnail
        the_title( '<h5>', '</h5>' );
        the_post_thumbnail( 'thumbnail' );
      endwhile;

      wp_reset_postdata();
    else :
     esc_html_e( 'No posts matched your criteria.', 'bricks' );
    endif;
  }
}

Gradient Control

The gradient control lets you define an unlimited number of gradients that you can apply to text, background, and as an overlay.

You can set the CSS selector in the control, adjust the angle between 0 and 360°, and set a color stop for each color.





class Prefix_Element_Gradient extends \Bricks\Element {
  // Set builder controls
  public function set_controls() {
    $this->controls['exampleGradient'] = [
      'tab' => 'content',
      'label' => esc_html__( 'Gradient', 'bricks' ),
      'type' => 'gradient',
      'css' => [
        [
          'property' => 'background-image',
        ],
      ],
    ];
  }

  // Render element HTML
  public function render() {
    echo get_bloginfo( 'name' );
  }
}

Apply Control

The apply control saves your settings. You can set the reload control property to true in order to trigger a builder reload after the “Apply” button has been clicked. We use it in the builder for settings like the template “Populate Content” or the “SEO” page settings.





$this->controls['apply'] = [
  'group' => 'template-preview',
  'type' => 'apply',
  'reload' => true,
  'label' => esc_html__( 'Apply preview', 'bricks' ),
];

Align Items Control (Flexbox)

Use the align-items control to allow users to set the align-items CSS property (alignment along the cross-axis) of your CSS flexbox layout.

There is also a justify-content control to allow users to set the alignment along the main axis of your CSS flexbox layout:

public function set_controls() {
  $this->controls['alignItems'] = [ // Setting key
    'tab'   => 'content',
    'label' => esc_html__( 'Align items', 'bricks' ),
    'type'  => 'align-items',
    'css'   => [
      [
        'property' => 'align-items',
        'selector' => '.flexbox-wrapper',
      ],
    ],
    // 'isHorizontal' => false,
    // 'exclude' => [
      // 'flex-start',
      // 'center',
      // 'flex-end',
      // 'space-between',
      // 'space-around',
      // 'space-evenly',
    // ],
  ];
}

Justify Content Control (Flexbox)

Use the justify-content control to allow users to set the justify-content CSS property (alignment along the main-axis) of your CSS flexbox layout.

There is also a align-items control to allow users to set the alignment along the cross axis of your CSS flexbox layout:

public function set_controls() {
  $this->controls['justifyContent'] = [
    'tab'   => 'content',
    'label' => esc_html__( 'Justify content', 'bricks' ),
    'type'  => 'justify-content',
    'css'   => [
      [
        'property' => 'justify-content',
        'selector' => '.flexbox-wrapper',
      ],
    ],
    // 'isHorizontal' => false,
    // 'exclude' => [
      // 'flex-start',
      // 'center',
      // 'flex-end',
      // 'space-between',
      // 'space-around',
      // 'space-evenly',
    // ],
  ];
}

Direction Control (Flexbox)

Use the direction control to allow users to set the flex-direction CSS property of your CSS flexbox layout.

public function set_controls() {
  $this->controls['direction'] = [ // Setting key
    'tab'   => 'content',
    'label' => esc_html__( 'Direction', 'bricks' ),
    'type'  => 'direction',
    'css'   => [
      [
        'property' => 'flex-direction',
        'selector' => '.flexbox-wrapper',
        // 'id'       => '', // Leave 'id' empty to apply to .flexbox-wrapper directly (@since 1.5.6)
      ],
    ],
  ];
}

Text Align Control

Use the text-align control to allow users to set the text-align CSS property like so:

public function set_controls() {
  $this->controls['textAlign'] = [ // Setting key
    'tab' => 'content',
    'label' => esc_html__( 'Text align', 'bricks' ),
    'type' => 'text-align',
    'css' => [
      [
        'property' => 'text-align',
        'selector' => '.text-wrapper',
      ],
    ],
  ];
}

Text Decoration Control

Use the text-decoration control to allow users to set the text-decoration CSS property like so:

public function set_controls() {
  $this->controls['textDecoration'] = [ // Setting key
    'tab' => 'content',
    'label' => esc_html__( 'Text decoration', 'bricks' ),
    'type' => 'text-decoration',
    'css' => [
      [
        'property' => 'text-decoration',
        'selector' => '.text-wrapper',
      ],
    ],
  ];
}

Text Transform Control

Use the text-transform control to allow users to set the text-transform CSS property like so:

public function set_controls() {
  $this->controls['textTransform'] = [ // Setting key
    'tab' => 'content',
    'label' => esc_html__( 'Text transform', 'bricks' ),
    'type' => 'text-transform',
    'css' => [
      [
        'property' => 'text-transform',
        'selector' => '.text-wrapper',
      ],
    ],
  ];
}

Working Custom Element Examples

These are my working custom elements examples. Understand them first.

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

use Bricks\Element;
use Bricks\Frontend;

class Flipbox_Element extends Element {
    public $category       = 'snn';
    public $name           = 'flipbox';
    public $icon           = 'ti-exchange-vertical';
    public $css_selector   = '.flip-container';
    public $scripts        = [];
    public $nestable       = true;
    public $nestable_areas = ['front', 'back'];

    public function get_label() {
        return esc_html__( 'Flipbox (Nestable)', 'bricks' );
    }

    public function set_controls() {
        // Flipbox Height Control.
        $this->controls['flipbox_height'] = [
            'tab'         => 'content',
            'type'        => 'number',
            'label'       => esc_html__( 'Flipbox Height', 'bricks' ),
            'default'     => 250,
            'min'         => 100,
            'max'         => 1000,
            'step'        => 1,
            'unit'        => 'px',
            'description' => "<br>
                <p data-control='info'>
                    Add 2 Column or 2 Block or 2 Div and create your own custom flipbox. Easy.
                </p>
            ",
        ];

        // Flipbox Perspective Control.
        $this->controls['flipbox_perspective'] = [
            'tab'         => 'content',
            'type'        => 'number',
            'label'       => esc_html__( 'Flipbox Perspective', 'bricks' ),
            'default'     => '1000px',
            'step'        => 1,
            'unit'        => 'px',
        ];

        // Flipbox Animation Control.
        $this->controls['flipbox_animation'] = [
            'tab'     => 'content',
            'label'   => esc_html__( 'Flipbox Animation', 'bricks' ),
            'type'    => 'select',
            'options' => [
                'left-to-right'         => esc_html__( 'Left to Right (Flip)', 'bricks' ),
                'right-to-left'         => esc_html__( 'Right to Left (Flip)', 'bricks' ),
                'top-to-bottom'         => esc_html__( 'Top to Bottom (Flip)', 'bricks' ),
                'bottom-to-top'         => esc_html__( 'Bottom to Top (Flip)', 'bricks' ),
                'fade'                  => esc_html__( 'Fade', 'bricks' ),
                'spin-flip'             => esc_html__( 'Spin Flip', 'bricks' ),
                'left-to-right-slide'   => esc_html__( 'Left to Right Slide', 'bricks' ),
                'right-to-left-slide'   => esc_html__( 'Right to Left Slide', 'bricks' ),
                'top-to-down-slide'     => esc_html__( 'Top to Down Slide', 'bricks' ),
                'bottom-to-top-slide'   => esc_html__( 'Bottom to Top Slide', 'bricks' ),
            ],
            'default' => 'left-to-right',
        ];
    }

    public function render() {
        // Retrieve settings.
        $height      = intval( $this->settings['flipbox_height'] ?? 250 );
        $perspective = intval( $this->settings['flipbox_perspective'] ?? 1000 );
        $animation   = $this->settings['flipbox_animation'] ?? 'left-to-right';

        // Set up root element attributes.
        $this->set_attribute( '_root', 'class', 'brxe-flipbox flip-container' );
        if ( ! empty( $this->attributes['_root']['id'] ) ) {
            $root_id = $this->attributes['_root']['id'];
        } else {
            $root_id = 'flipbox-' . uniqid();
            $this->set_attribute( '_root', 'id', $root_id );
        }

        // Render the flipbox container.
        echo '<div ' . $this->render_attributes('_root') . '>';
            echo '<style>
                #' . esc_attr( $root_id ) . ' {
                    width: 100%;
                    height: ' . esc_attr( $height ) . 'px;
                    perspective: ' . esc_attr( $perspective ) . 'px;
                    cursor: pointer;
                }
                #' . esc_attr( $root_id ) . ' .flip-box {
                    width: 100%;
                    height: 100%;
                    position: relative;
                    transform-style: preserve-3d;
                    transition: transform 0.6s ease-in-out;
                }';

                // Animation-specific CSS.
                if ( $animation === "fade" ) {
                    echo '
                    #' . esc_attr( $root_id ) . ' .flip-box > div {
                        width: 100%;
                        height: 100%;
                        position: absolute;
                        transition: opacity 0.6s ease-in-out;
                    }
                    #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(1) {
                        opacity: 1;
                    }
                    #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                        opacity: 0;
                    }
                    #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(1),
                    #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(1) {
                        opacity: 0;
                    }
                    #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(2),
                    #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(2) {
                        opacity: 1;
                    }';
                } elseif ( in_array( $animation, ['left-to-right-slide', 'right-to-left-slide', 'top-to-down-slide', 'bottom-to-top-slide'] ) ) {
                    echo '
                    #' . esc_attr( $root_id ) . ' .flip-box > div {
                        width: 100%;
                        height: 100%;
                        position: absolute;
                        transition: transform 0.6s ease-in-out;
                    }';
                    if ( $animation === "left-to-right-slide" ) {
                        echo '
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(1) {
                            transform: translateX(0);
                        }
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                            transform: translateX(100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(1),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(1) {
                            transform: translateX(-100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(2),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(2) {
                            transform: translateX(0);
                        }';
                    } elseif ( $animation === "right-to-left-slide" ) {
                        echo '
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(1) {
                            transform: translateX(0);
                        }
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                            transform: translateX(-100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(1),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(1) {
                            transform: translateX(100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(2),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(2) {
                            transform: translateX(0);
                        }';
                    } elseif ( $animation === "top-to-down-slide" ) {
                        echo '
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(1) {
                            transform: translateY(0);
                        }
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                            transform: translateY(-100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(1),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(1) {
                            transform: translateY(100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(2),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(2) {
                            transform: translateY(0);
                        }';
                    } elseif ( $animation === "bottom-to-top-slide" ) {
                        echo '
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(1) {
                            transform: translateY(0);
                        }
                        #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                            transform: translateY(100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(1),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(1) {
                            transform: translateY(-100%);
                        }
                        #' . esc_attr( $root_id ) . ':hover .flip-box > div:nth-of-type(2),
                        #' . esc_attr( $root_id ) . ':focus .flip-box > div:nth-of-type(2) {
                            transform: translateY(0);
                        }';
                    }
                } else {
                    // Flip animations.
                    $containerHoverTransform = "";
                    $backTransform = "";
                    if ( $animation === "left-to-right" ) {
                        $containerHoverTransform = "rotateY(180deg)";
                        $backTransform = "rotateY(180deg)";
                    } elseif ( $animation === "right-to-left" ) {
                        $containerHoverTransform = "rotateY(-180deg)";
                        $backTransform = "rotateY(-180deg)";
                    } elseif ( $animation === "top-to-bottom" ) {
                        $containerHoverTransform = "rotateX(180deg)";
                        $backTransform = "rotateX(180deg)";
                    } elseif ( $animation === "bottom-to-top" ) {
                        $containerHoverTransform = "rotateX(-180deg)";
                        $backTransform = "rotateX(-180deg)";
                    } elseif ( $animation === "spin-flip" ) {
                        $containerHoverTransform = "rotateY(180deg) rotateZ(360deg)";
                        $backTransform = "rotateY(180deg)";
                    }
                    echo '
                    #' . esc_attr( $root_id ) . ' .flip-box > div {
                        width: 100%;
                        height: 100%;
                        position: absolute;
                        backface-visibility: hidden;
                        display: flex;
                    }
                    #' . esc_attr( $root_id ) . ' .flip-box > div:nth-of-type(2) {
                        transform: ' . esc_attr( $backTransform ) . ';
                    }
                    #' . esc_attr( $root_id ) . ':hover .flip-box,
                    #' . esc_attr( $root_id ) . ':focus .flip-box {
                        transform: ' . esc_attr( $containerHoverTransform ) . ';
                    }';
                }
                echo '
                .iframe #' . esc_attr( $root_id ) . ' .flip-box > div {
                    position: relative;
                    backface-visibility: visible;
                }
            </style>';
            // Render inner container without reusing _root attributes.
            echo '<div class="flip-box">';
                echo Frontend::render_children( $this );
            echo '</div>';
        echo '</div>';
    }
}
?>

This gets registered in the functions.php like this;

\Bricks\Elements::register_element(SNN_PATH . 'includes/elements/flip-box.php');
<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

use Bricks\Element;

class Prefix_Element_Toggle_Text extends Element {
    public $category  = 'snn';
    public $name      = 'toggle-text';
    public $icon      = 'ti-text';
    public $scripts   = []; 
    public $nestable  = false;

    public function get_label(): string {
        return esc_html__( 'Read More and Toggle Text', 'bricks' );
    }

    public function set_controls(): void {
        $this->controls['text_content'] = [
            'tab'           => 'content',
            'label'         => esc_html__( 'Text Content', 'bricks' ),
            'type'          => 'editor',
            'default'       => esc_html__( 'Lorem ipsum dolor sinan amet...', 'bricks' ),
            'inlineEditing' => true,
        ];

        $this->controls['text_height'] = [
            'tab'     => 'content',
            'label'   => esc_html__( 'Text Height (px)', 'bricks' ),
            'type'    => 'number',
            'default' => 100,
            'min'     => 0,
        ];

        $this->controls['button_selector'] = [
            'tab'         => 'content',
            'label'       => esc_html__( 'Native Button Selector (ID)', 'bricks' ),
            'type'        => 'text',
            'default'     => '',
            'placeholder' => '#my-button',
            'description' => "
                <p data-control='info'>
                    Add a button and copy the ID here to make the toggle work.<br><br>
                    Button icon animate CSS: <br>
                    %root%.active-toggle-text i{ <br>
                        rotate:180deg; <br>
                    }
                </p>
            ",
        ];

        $this->controls['text_typography'] = [
            'tab'   => 'style',
            'label' => esc_html__( 'Text Typography', 'bricks' ),
            'type'  => 'typography',
            'css'   => [
                [
                    'property' => 'typography',
                    'selector' => '.toggle-text-content',
                ],
            ],
        ];
    }

    public function render(): void {
        $unique_class = 'toggle-text-' . uniqid();
        $this->set_attribute( '_root', 'class', [ 'toggle-text-wrapper', $unique_class ] );

        $text_content    = $this->settings['text_content'] ?? esc_html__( 'Your content goes here...', 'bricks' );
        $text_height     = $this->settings['text_height'] ?? 100;
        $button_selector = $this->settings['button_selector'] ?? '';

        ?>
        <style>
            .toggle-text-wrapper {
                margin: 20px 0;
            }
            .toggle-text-content {
                overflow: hidden;
                transition: max-height 0.3s ease;
            }
        </style>

        <div <?php echo $this->render_attributes( '_root' ); ?>>
            <div class="toggle-text-content">
                <?php echo $text_content; ?>
            </div>
        </div>

        <script>
            document.addEventListener("DOMContentLoaded", function() {
                const container = document.querySelector(".<?php echo esc_js( $unique_class ); ?>");
                if (!container) return;

                const content = container.querySelector(".toggle-text-content");
                if (!content) return;

                <?php if ( ! empty( $button_selector ) ) : ?>
                    const button = document.querySelector(<?php echo json_encode( $button_selector ); ?>);
                    if (!button) return;

                    const collapsedHeight = <?php echo json_encode( $text_height ); ?>;
                    content.style.maxHeight = collapsedHeight + "px";

                    let isExpanded = false;

                    button.addEventListener("click", function() {
                        if (isExpanded) {
                            content.style.maxHeight = collapsedHeight + "px";
                            button.classList.remove("active-toggle-text");
                            button.setAttribute("aria-expanded", "false");
                        } else {
                            content.style.maxHeight = content.scrollHeight + "px";
                            button.classList.add("active-toggle-text");
                            button.setAttribute("aria-expanded", "true");
                        }
                        isExpanded = !isExpanded;
                    });
                <?php else : ?>
                    // console.warn("Button selector is not defined.");
                <?php endif; ?>
            });
        </script>
        <?php
    }

    public static function render_builder(): void {
        ?>
        <script type="text/x-template" id="tmpl-bricks-element-toggle-text">
            <component :is="tag">
                <div v-if="element.settings.text_content" class="toggle-text-content" v-html="element.settings.text_content"></div>
                <bricks-element-children :element="element"/>
            </component>
        </script>
        <?php
    }
}
?>

registered like on fucntions.php;

\Bricks\Elements::register_element(SNN_PATH . 'includes/elements/read-more-toggle-text.php');
<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

use Bricks\Element;

class Svg_Text_Path_Element extends Element {
    public $category     = 'snn';
    public $name         = 'svg-text-path';
    public $icon         = 'ti-text';
    public $css_selector = '.svg-text-path-wrapper';
    public $scripts      = []; // No external scripts required
    public $nestable     = false;

    public function get_label() {
        return esc_html__( 'SVG Text Path', 'bricks' );
    }

    public function set_control_groups() {
        // Define control groups if necessary.
    }

    public function set_controls() {
        // Text control for the text to be displayed along the path.
        $this->controls['text'] = [
            'tab'     => 'content',
            'type'    => 'text',
            'default' => esc_html__( 'Your Text Here', 'bricks' ),
        ];

        // Select control for choosing the SVG path shape.
        $this->controls['svg_option'] = [
            'tab'     => 'content',
            'type'    => 'select',
            'options' => [
                'wave'   => esc_html__( 'Wave', 'bricks' ),
                'arc'    => esc_html__( 'Arc', 'bricks' ),
                'circle' => esc_html__( 'Circle', 'bricks' ),
                'elypse' => esc_html__( 'Elypse', 'bricks' ),
                'spiral' => esc_html__( 'Spiral', 'bricks' ),
            ],
            'default' => 'wave',
        ];

        // Control for custom SVG upload.
        $this->controls['custom_svg'] = [
            'tab'         => 'content',
            'type'        => 'svg',
            'label'       => esc_html__( 'Custom SVG', 'bricks' ),
            'description' => esc_html__( 'Upload your own SVG path. <br>Create a 510px x 170px transparent rectangle, draw your path inside it, group and export it.', 'bricks' ),
        ];

        // New slider control to rotate the text (in degrees).
        $this->controls['rotate'] = [
            'tab'     => 'content',
            'type'    => 'slider',
            'label'   => esc_html__( 'Rotate Text', 'bricks' ),
            'units'   => [
                'deg' => [
                    'min'  => 0,
                    'max'  => 360,
                    'step' => 1,
                ],
            ],
            'default' => '0deg',
        ];

        // New number control for the starting point (startOffset) of the text path.
        $this->controls['start_offset'] = [
            'tab'     => 'content',
            'type'    => 'number',
            'label'   => esc_html__( 'Text Start Offset', 'bricks' ),
            'default' => 0,
            'min'     => 0,
            'max'     => 100,
            'step'    => 1,
            'unit'    => 'px',
        ];

        // New number control for word spacing.
        $this->controls['word_spacing'] = [
            'tab'     => 'content',
            'type'    => 'number',
            'label'   => esc_html__( 'Word Spacing', 'bricks' ),
            'default' => 0,
            'min'     => 0,
            'max'     => 50,
            'step'    => 0.1,
            'unit'    => 'px',
        ];
        
        // New control for path stroke color.
        $this->controls['path_stroke_color'] = [
            'tab'     => 'content',
            'type'    => 'color',
            'label'   => esc_html__( 'Path Stroke Color', 'bricks' ),
            'default' => '#000000',
        ];
        
        // New control for path stroke width.
        $this->controls['path_stroke_width'] = [
            'tab'     => 'content',
            'type'    => 'slider',
            'label'   => esc_html__( 'Path Stroke Width', 'bricks' ),
            'units'   => [
                'px' => [
                    'min'  => 0,
                    'max'  => 100,
                    'step' => 0.1,
                ],
            ],
            'default' => '0px',
        ];
    }

    public function render() {
        $text       = isset( $this->settings['text'] ) ? $this->settings['text'] : '';
        $svg_option = isset( $this->settings['svg_option'] ) ? $this->settings['svg_option'] : 'wave';

        // Sanitize settings.
        $text = is_array( $text ) ? '' : esc_html( $text );

        $rotate       = isset( $this->settings['rotate'] ) ? floatval( $this->settings['rotate'] ) : 0;
        $start_offset = isset( $this->settings['start_offset'] ) ? floatval( $this->settings['start_offset'] ) : 0;
        $word_spacing = isset( $this->settings['word_spacing'] ) ? floatval( $this->settings['word_spacing'] ) : 0;
        
        // Retrieve stroke color and stroke width.
        $path_stroke_color = isset( $this->settings['path_stroke_color'] ) ? $this->settings['path_stroke_color'] : '#000000';
        if ( is_array( $path_stroke_color ) ) {
            $path_stroke_color = !empty( $path_stroke_color['rgba'] )
                ? $path_stroke_color['rgba']
                : ( !empty( $path_stroke_color['hex'] ) ? $path_stroke_color['hex'] : '#000000' );
        }
        
        $stroke_width_value = isset( $this->settings['path_stroke_width'] ) ? $this->settings['path_stroke_width'] : '2px';
        if ( is_array( $stroke_width_value ) ) {
            $val  = isset( $stroke_width_value['value'] ) ? $stroke_width_value['value'] : '2';
            $unit = isset( $stroke_width_value['unit'] ) ? $stroke_width_value['unit'] : 'px';
            $stroke_width_value = $val . $unit;
        }

        // Use a CSS class for the <text> element instead of inline transform attributes.
        $text_attrs = ' class="svg-text-path-text"';

        // Set attributes on the root element.
        $this->set_attribute( '_root', 'class', 'brxe-svg-text-path' );
        if ( ! empty( $this->attributes['_root']['id'] ) ) {
            $root_id = $this->attributes['_root']['id'];
        } else {
            $root_id = 'svg-text-path-' . uniqid();
            $this->set_attribute( '_root', 'id', $root_id );
        }

        // Set attributes on the child element so Bricks applies its dynamic styles.
        $this->set_attribute( 'child', 'class', 'svg-text-path-wrapper' );

        // Preset SVG markups with placeholders for text attributes, start offset and text.
        $preset_svgs = [
            'wave' => '<svg viewBox="0 0 250 42.4994" xmlns="http://www.w3.org/2000/svg">
  <path d="M0,42.2494C62.5,42.2494,62.5.25,125,.25s62.5,41.9994,125,41.9994" id="e-path-a9421d5"></path>
  <text' . $text_attrs . '>
    <textPath id="e-text-path-a9421d5" href="#e-path-a9421d5" startOffset="%s">%s</textPath>
  </text>
</svg>',
            'arc' => '<svg viewBox="0 0 250.5 125.25" xmlns="http://www.w3.org/2000/svg">
  <path d="M.25,125.25a125,125,0,0,1,250,0" id="e-path-1bb1e70"></path>
  <text' . $text_attrs . '>
    <textPath id="e-text-path-1bb1e70" href="#e-path-1bb1e70" startOffset="%s">%s</textPath>
  </text>
</svg>',
            'circle' => '<svg viewBox="0 0 250.5 250.5" xmlns="http://www.w3.org/2000/svg">
  <path d="M.25,125.25a125,125,0,1,1,125,125,125,125,0,0,1-125-125" id="e-path-5de0159"></path>
  <text' . $text_attrs . '>
    <textPath id="e-text-path-5de0159" href="#e-path-5de0159" startOffset="%s">%s</textPath>
  </text>
</svg>',
            'elypse' => '<svg viewBox="0 0 250.5 125.75" xmlns="http://www.w3.org/2000/svg">
  <path d="M.25,62.875C.25,28.2882,56.2144.25,125.25.25s125,28.0382,125,62.625-55.9644,62.625-125,62.625S.25,97.4619.25,62.875" id="e-path-6995a9f"></path>
  <text' . $text_attrs . '>
    <textPath id="e-text-path-6995a9f" href="#e-path-6995a9f" startOffset="%s">%s</textPath>
  </text>
</svg>',
            'spiral' => '<svg viewBox="0 0 250.4348 239.4454" xmlns="http://www.w3.org/2000/svg">
  <path d="M.1848,49.0219a149.3489,149.3489,0,0,1,210.9824-9.8266,119.479,119.479,0,0,1,7.8613,168.786A95.5831,95.5831,0,0,1,84,214.27a76.4666,76.4666,0,0,1-5.0312-108.023" id="e-path-00f165a"></path>
  <text' . $text_attrs . '>
    <textPath id="e-text-path-00f165a" href="#e-path-00f165a" startOffset="%s">%s</textPath>
  </text>
</svg>',
        ];

        echo '<div ' . $this->render_attributes( '_root' ) . '>';
            echo '<style>
  #' . esc_attr( $root_id ) . ' svg {
    width: 100% ;
    height: auto ;
    position: relative ;
    left: 0 ;
    overflow: visible ;';
            if ( $rotate !== 0 ) {
                echo ' transform: rotate(' . esc_attr( $rotate ) . 'deg); transform-origin: center;';
            }
            echo ' }
  #' . esc_attr( $root_id ) . ' svg path {
    fill: transparent ;
  }
  #' . esc_attr( $root_id ) . ' svg textPath {
    fill: currentColor ;
    stroke: ' . esc_attr( $path_stroke_color ) . ' ;
    stroke-width: ' . esc_attr( $stroke_width_value ) . ' ;
  }
  #' . esc_attr( $root_id ) . ' svg text.svg-text-path-text {';
            if ( $word_spacing !== 0 ) {
                echo ' word-spacing: ' . esc_attr( $word_spacing ) . 'px;';
            }
            echo ' }
</style>';
            echo '<div ' . $this->render_attributes( 'child' ) . '>';
                if ( isset( $this->settings['custom_svg']['url'] ) && ! empty( $this->settings['custom_svg']['url'] ) ) {
                    // For frontend rendering we load the full SVG content and inject the text along its path.
                    $custom_svg_content = file_get_contents( esc_url( $this->settings['custom_svg']['url'] ) );
                    
                    // Use DOMDocument to modify the custom SVG.
                    libxml_use_internal_errors(true);
                    $doc = new DOMDocument();
                    $doc->loadXML( $custom_svg_content );
                    libxml_clear_errors();
                    
                    // Get the first <path> element.
                    $paths = $doc->getElementsByTagName('path');
                    if ( $paths->length > 0 ) {
                        $path = $paths->item(0);
                        // Remove stroke attribute if present.
                        if ( $path->hasAttribute('stroke') ) {
                            $path->removeAttribute('stroke');
                        }
                        // Ensure the path has an ID.
                        if ( ! $path->hasAttribute('id') ) {
                            $generated_id = 'custom-svg-path-' . uniqid();
                            $path->setAttribute('id', $generated_id);
                        } else {
                            $generated_id = $path->getAttribute('id');
                        }
                        
                        // Create a <text> element and assign the CSS class.
                        $textElement = $doc->createElement('text');
                        $textElement->setAttribute('class', 'svg-text-path-text');
                        
                        // Create the <textPath> element with the provided text.
                        $textPathElement = $doc->createElement('textPath', htmlspecialchars( $text ));
                        $textPathElement->setAttribute('href', '#' . $generated_id);
                        $textPathElement->setAttribute('startOffset', esc_attr( $start_offset ) . '%');
                        
                        // Append the textPath to the text element.
                        $textElement->appendChild( $textPathElement );
                        
                        // Append the text element to the main <svg> element.
                        $svgs = $doc->getElementsByTagName('svg');
                        if ( $svgs->length > 0 ) {
                            $svg = $svgs->item(0);
                            $svg->appendChild( $textElement );
                        }
                        
                        echo $doc->saveXML();
                    } else {
                        // If no path is found, output the custom SVG as is.
                        echo $custom_svg_content;
                    }
                } else {
                    $preset_svg = isset( $preset_svgs[ $svg_option ] ) ? $preset_svgs[ $svg_option ] : $preset_svgs['wave'];
                    echo sprintf( $preset_svg, sprintf( '%s%%', esc_attr( $start_offset ) ), $text );
                }
            echo '</div>';
        echo '</div>';
    }
}
?>

registered like easily;

\Bricks\Elements::register_element(SNN_PATH . 'includes/elements/svg-text-path.php');

Upcoming Course

I am planning a professional WordPress and Bricks Builder course. If you are Interested register to this newsletter.