Here at CNDLS, we recently redesigned the website for our masters program, the Master of Arts in Learning and Design (MLD). We chose to build a custom WordPress theme so that we could have maximum flexibility in the design of the site, but still have the site integrated with a Content Management System (CMS) such as WordPress so that the program administrator can update the website without any web development expertise.

The website development went well, and we launched the new site successfully! The theme development strategy that I employed, which I’d used at marketing agencies where I’ve worked in the past, is to build a template for each unique type of page for the website. Each template, in turn, has unique custom fields in the Edit Page screen where the content editor can edit the page content. The page layout, however, is by and large determined by the PHP/HTML and CSS associated with each template. We used the popular and well-supported Advanced Custom Fields Pro plugin to easily add specific custom fields for each theme template.

Screenshot of a WordPress Edit Page screen using custom fields.

This approached works very well, but there are two disadvantages:

  1. WordPress, by default, has an administration area (WP-Admin) that is separate from the web pages that a user sees when they visit the website (website front-end). Thus, the content editor doesn’t see a preview of what the front-end will actually look like as she is changing the content in the Edit Page screen, and has to switch back and forth between the WP-Admin and website front-end in different tabs as she makes changes to the page.
  2. Although the content editor can edit the content of the page, the page layout is determined by the page template, and so in order to change the layout of a page or build a new page with a new layout, a web developer is required to edit the existing template or build a new template.

At CNDLS, we have a lot of web development projects happening, with a relatively few number of web developers on staff. Therefore, while reflecting on the new MLD website, we determined that as the website evolved over time, we should set up a generic template which would allow the content editor to build new page designs and change the layout as she wished, which would save the web developers a lot of work in the future.

At first, I thought that I could continue with the custom fields strategy, and use ACF Pro’s Flexible Layout field to allow the content editor to design her own layout using some of the “content blocks” that we had already defined in our theme. However, as I started to develop a template which allowed the editor to add rows, columns, and margins, the number of fields quickly became unwieldy, and it was confusing to correlate those fields with how the page looked on the front-end.

I then looked into Elementor, a page-builder plugin with a front-end page editor which allows editors to drag and drop widgets (blocks of content) into rows and columns on a live page preview. I thought that if I could define custom widgets which corresponded to the components of our custom theme design, this would fulfill all our goals: allow the editor to layout a page with an intuitive live preview while at the same time forcing the editor to use custom blocks which match our theme style.

After perusing a few blog posts, reading Elementor’s Developer API docs, examining the Elementor source code itself, and much coding, debugging, and experimentation, I was able to customize Elementor enough so that it works smoothly with our custom theme! Below are some screenshots of our customized version of Elementor in action.

Custom widgets for the MLD theme appearing in their own section in Elementor’s widget gallery.

Screenshot of Elementor page editor sidebar and live page preview showing the Schedule widget.

Screenshot of the controls for the Schedule widget in the Content tab of the widget editor interface.

Technical Implementation Details (for Web Developers)

For those who are web developers, here is a step-by-step guide for how I customized Elementor to work with our theme.

Elementor has a pretty good collection of action and filter hooks, which allow you to customize how it functions without actually overwriting it’s source, so that updates won’t break your customization. I therefore used Elementor’s hooks and extended Elementor’s base classes to customize it to work with our theme.

Creating a Custom Widget by Extending Widget_Base

The most important customization task was to create custom widgets that corresponded to the content blocks that we already had in our theme. To create a custom widget, you extend Elementor’s Widget_Base class. Below is the PHP code for defining a custom widget called Schedule. The comments in the snippet explain what each piece does.

Pulling Data from Elementor in Your PHP Template

Here is the code for my PHP template for the Schedule widget, which is included in the render() function of the Schedule class. Note how the template accesses the data which corresponds to the widget controls which we described in _register_controls().

Telling WordPress About Your Custom Elementor Widgets

In order to tell WordPress to register my custom Elementor widget, I need to tell it to run the class definition file for each of my widgets. I also need to register my custom widget category with Elementor, so that all my custom widgets appear in the same section. To achieve this, I add the following code to my theme’s functions.php file (or a file included in functions.php).

Incorporating Customized Theme Spacing Into Elementor Sections and Columns

Once I created our theme’s custom widgets, I was confronted by another problem: in our theme, in order to maintain consistency throughout the site, we have a discrete number of spacing (margin and padding) levels between sections of content. In general, the spacing levels correspond to the heading level of the section: sections with an <h2> have more spacing between them than a section with an <h3>, etc. In addition, a section with a given heading level will have different spacing values depending on whether the browser window is mobile width, tablet width, or desktop width. With the previous fixed template approach to our theme, these spacing variations between sections could be coded using HTML and CSS, and the content editor didn’t need to worry about these details. However, now that we are enabling the content editor to build a page layout, these are elements that the content editor needs to be able to control.

For page layout, Elementor uses Sections (the equivalent of rows), and Columns. Each Section or Column has a Style tab which allows an editor to manually set margin and padding for the Section or Column at three screen sizes (mobile, tablet, and desktop). However, asking the editor to, for example, remember the exact numeric value of the margin-bottom property for a Section that contains an <h2> heading at mobile, tablet, and desktop sizes, and asking the content editor to enter those values each time for every Section or Column would be overly difficult and tedious for the editor, and would inevitably lead to inconsistency among Section and Column margins throughout the site.

Therefore, I decided that I needed to add custom controls to Elementor’s Section and Column elements which allow the editor to choose from a list of predefined spacing levels.

This task proved to be trickier than defining custom widgets, mainly because, while I could define the controls and template of a custom widget from scratch, with Elementor’s Section and Column elements I was only trying to add a few additional features to them, while at the same time preserving all their default functionality.

Luckily, I located the Elementor hooks which allowed me to do just that.

Adding Custom Controls to Section and Column Elements

The first thing I had to do was add custom controls to Elementor’s Section and Column elements. The code below shows you how I used Elementor’s hooks to add the custom controls.

This resulted in the following front-end control structure:

The Spacing control section of a Section element. On top is the "Do Not Apply Spacing" master override control. Below that is a spacing item, whose property is set to Margin, Direction is set to Bottom, and Heading Level is set to H3. At the bottom of the controls for the spacing item there is a subsection called Zero Spacing on Check Screen Sizes, which has the extra small and small screens checked.

Custom Spacing Controls for the Elementor Spacing Element

The “Do Not Apply Spacing Classes” control at the top is like a master override switch which will disable any custom spacing on the given Section or Column. The reason that I had to add this switch is that the current Elementor repeater control will not let you delete the last repeater item in a set once you have added at least one repeater item. Therefore, the master switch allows to you override the spacing of the last remaining spacing item if you in fact would like to delete it (but can’t).

The “Zero Spacing on Checked Screen Sizes” switches allows for situations where, on mobile, because of stacking behavior, you want the Columns to have margin-bottoms but not the Sections, while on desktop, you want the Section to have a margin-bottom but not the Columns. These switches allow you to select for which screen sizes the custom spacing class will be applied.

I use the repeater field in the first place so that a user can add multiple custom spacing classes to a Section or Column.

Adding the Custom Spacing Classes to Section and Column Elements When the Page is Rendered

In order to add the custom spacing classes to the Section and Column elements that correspond to the values of the custom spacing controls, I use the action hook ‘before_render’. This allowed me to fetch the control values, convert them to classes (for which I have created style rules in my theme’s CSS), and add those classes to the list of classes that Elementor would already apply to a Section or Column. See code snippet below.

Adding the Custom Spacing Classes In The Page Editor Preview Pane

In order to generate a preview of the page you are editing and have it quickly respond to changes in element controls, Elementor makes use of Backbone.js templates for rendering the page preview. If a custom widget does not define a Backbone.js template for how the widget should be displayed in the editor preview, Elementor sends AJAX calls to the server to have it’s PHP template be used to update the live preview (this is why I did not need to add a Backbone.js template to my custom widget classes). However, if an Elementor Element does define a Backbone.js template, it is this template, and not the PHP template, that is used to make live updates to the editor preview. Because Elementor had already defined a Backbone.js template for the Section and Column elements, I needed to modify the Section and Column Backbone.js templates to add the custom spacing classes to them, so that the elements’ spacing would change in the page preview as the user changes the spacing controls. To do this, I use the ‘print_template’ filter hook. Although I didn’t have much experience with Backbone.js templates, it appears that they work very similar to PHP templates, except that you use <# instead of <?php, and you write in Javascript instead of PHP. It appears that Elementor makes the settings variable available to all Backbone.js templates in order to access the control values.

Overriding Some Elementor Styles in SCSS/CSS

Lastly, I had to override some of Elemetor’s default styles so that the markup it generates would work with the Bootstrap 4 grid that I use for the MLD theme. The SCSS code is below.

Thanks for reading the post! I hope these details help you in integrating Elementor into your own custom theme, as I know I could’ve used a post like this when I started on this project!