Use Selectors in Elementor Widgets to Control CSS

Use Selectors in Elementor Widgets to Control CSS

Share on facebook
Share on twitter
Share on pinterest

Hey! Want to learn more about developer features in Elementor? This one gets only a handful of mentions in the docs. Therefore I believe it’s useful to share all I know about it. When creating a control, you may add an optional parameter called selector or selectors that will manipulate CSS of the live preview on the fly. If you ask me, selectors in Elementor is what made this builder what it is today. It’s the modern-day interpretation of WYSIWYG, and by leveraging it, developers can save hours of user frustration. Why? Saving the editor and reloading another tab is no longer necessary for every minuscule change, such as:

One can learn a lot from Elementor’s code, and that’s what I did to gain insight into what’s possible with selectors. It is useful for a lot more than you think! With some creativity, it’s possible to push the limits to avoid writing additional handlers. In production, more than half of the controls in our SVG Divider for Elementor rely heavily on the selectors parameter. Because of this, I never actually had to use the _content_template() method (to build live the preview with JS). As you might have guessed, this article is for developers of Elementor extensions and requires PHP and CSS knowledge.

Where to add ‘selector’ or ‘selectors’ in Elementor?

When you create a Widget, you register controls for it. All the examples here go into the Control Setting array of the add_control() method. See more about creating a new Widget and its Controls at the Elementor developer documentation.

The name of the parameter is selector for Group Controls. It’s just a string you’d write in CSS before the declarations in { ... } . There is a better one that I focus on: selectors, which is a more elaborate structure, an array of key-value pairs translating to selectors-declarations.

The output ends up in a file, so it’s not inlined once the user leaves the editor! Something like:


Curly brace variables

The optional but vital heart of the matter is using dynamic values. These are not part of a template engine, Elementor performs a search and replace on them. You’ll see the examples below, but first, a reference table for your convenience.

CSS Selectors (key in the selectors array)

{{WRAPPER}} Behind the scenes, it translates to something like:

.elementor-50 .elementor-element.elementor-element-092e113

  • Use it almost always to make sure you select things only in a particular widget instance.
  • It matters whether or not you follow this with a space.
{{ID}} Get just the ID of the Widget (such as 092e113).
Use the selector-declaration pair in the appropriate Media Query. If present, the selector has to start with this.

You mostly only need this when using another setting’s value. Responsive Controls resolve this automatically. However, this can apply/disable the effect of an otherwise not responsive setting, on some devices.

It specifies the device to apply the rules at, and on anything smaller (default responsive behavior). However, when suffixed with a plus sign (+), they will apply on at least that device’s resolution or larger.

Declarations (value in the selectors array)

You can use these anywhere in CSS properties and values.

{{VALUE}} Many controls return a single value, access it with this. The selectors_dictionary can change this on the fly.
{{SIZE}} and {{UNIT}} Numerical controls give you the size and the unit separately, so these usually appear glued to each other, but not always.
{{CURRENT_ITEM}} Used in Repeater Controls.
Obtain values from other controls using VALUE, SIZE, UNIT, or any named value. Suffixes available are _tablet and _mobile to access responsive data.
It’s for each direction value of a Dimensions Control.
{{URL}} or other names Used to access named properties in “Multiple Controls” that return an array, such as Media Control.
{{setting.SIZE || 5}}
{{a_tablet.SIZE || "fallback"}}
{{a.SIZE || b.DEFAULT}}
{{a.URL || b.URL}}
{{a.VALUE || a_tablet.DEFAULT}}
Use a fallback value.

  • The setting must be named: include the dot and add any property from above.
  • The left side works best with controls that become empty when cleared (Slider and URL come to mind). Untouched responsive controls are also empty by default.
  • The right side can be a hardcoded number, string, or a named setting.
  • DEFAULT only works with those controls that have a VALUE. You can tap into mobile defaults if any.

Simple examples

Value from a color picker:

'selectors' => [
    '{{WRAPPER}} .elementor-svg-divider-basic-text' => 'color: {{VALUE}}'

Size with and without unit:

'selectors' => [
    '{{WRAPPER}} svg.sde-classic' =>
        'height: {{SIZE}}{{UNIT}}; stroke-width: {{SIZE}};'

Create padding from dimensions:

'selectors' => [
    '{{WRAPPER}} .elementor-svg-divider-basic-button' =>
            {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'

Background image of a slide in a slider (using repeater):

'selectors' => [
    '{{WRAPPER}} {{CURRENT_ITEM}} .swiper-slide-bg' =>
        'background-image: url({{URL}})',

Targeting some class on the body, like .elementor-msie, body.rtl or body:not(.rtl) or even .woocommerce:

'selectors' => [
    'body:not(.rtl) {{WRAPPER}} .dialog-close-button' => 'right: {{SIZE}}{{UNIT}}',
    'body.rtl {{WRAPPER}} .dialog-close-button' => 'left: {{SIZE}}{{UNIT}}',

Values from other settings

It’s effortless to obtain values from other controls, once you know how. If two settings are contributing to the same thing, feel free to use the same selectors array, in addition to digging into each other’s values.

'selectors' => [
    '{{WRAPPER}} svg.sde-classic' =>
        'stroke-dasharray: {{dash_length.SIZE}} {{whitespace_length.SIZE}};'

Note that this gets values for specific devices when the target is responsive. Here is an advanced example, based on Elementor pro:

'selectors' => [
    '(desktop).elementor-msie {{WRAPPER}} .elementor-portfolio-item' =>
        'width: calc( 100% / {{columns.SIZE}} );
        border: {{SIZE}}px solid transparent',
    '(tablet).elementor-msie {{WRAPPER}} .elementor-portfolio-item' =>
        'width: calc( 100% / {{columns_tablet.SIZE}} );
        border: {{SIZE}}px solid transparent',
    '(mobile).elementor-msie {{WRAPPER}} .elementor-portfolio-item' =>
        'width: calc( 100% / {{columns_mobile.SIZE}} );
        border: {{SIZE}}px solid transparent',

Dynamic values in CSS properties

In lieu of a better real-world example, the following comes from an Align control with left/center/right options. I know this doesn’t look clean, but Elementor core itself used it like this. The margin-center CSS property doesn’t exist, so it gets ignored by browsers.

// Somewhere else
$sde_selector = '{{WRAPPER}} .sde';
// In a control
'selectors' => [
    $sde_selector => 'margin: 0 auto; margin-{{VALUE}}: 0;'

The point of this example is to open your eyes. As you see, the dynamic values can form property names as well.

You can re-use selectors by putting them in a variable, especially if they are long. However, you cannot use values from other settings in the CSS selector part, only in the declaration!

(!) The Selectors Dictionary

A better alternative to the above oddity is the undocumented holy grail, the selectors_dictionary property. In a nutshell, it’s like a switch statement for selectors! If the value of the control is X, use Z instead. It’s incredibly useful when the CSS you want to write has nothing in common with the options of your control.

'selectors_dictionary' => [
    'left' => 'margin-right: auto',
    'center' => 'margin: 0 auto',
    'right' => 'margin-left: auto',
'selectors' => [
    '{{WRAPPER}} .sde' => '{{VALUE}}',

That’s it. The only caveat is that it overrides the {{VALUE}} curly brace variable, and you can’t use the original value anymore (not even in the dictionary itself). If you had another selector-declaration pair that relied on the initial value, you’d be in trouble:

'selectors' => [
    '{{WRAPPER}} .sde' => '{{VALUE}}',
    '{{WRAPPER}}.elementor-sde-scale-the-cropped .sde-cropping-allow .sde' =>
        'transform-origin: {{VALUE}} 0;'

Misuse leads to an abomination like transform-origin: margin: 0 auto 0; instead of the original transform-origin: center 0;, so be careful. There is no need to map every possible value, though!

Of course, you can still use the selectors dictionary to translate just the CSS values in the declaration, such as:

'selectors_dictionary' => [
    'top' => 'flex-start',
    'middle' => 'center',
    'bottom' => 'flex-end',
'selectors' => [
    '{{WRAPPER}} .elementor-price-table__currency' => 'align-self: {{VALUE}}',

Or use it for multiple declarations expanded from single-word options:

'selectors_dictionary' => [
    'left' => 'right: auto; left: 0',
    'right' => 'left: auto; right: 0',
'selectors' => [
    '{{WRAPPER}}.elementor-wc-products ul.products li.product span.onsale' =>

Don’t forget CSS variables and calc!

One thing I like about programming is that it’s like a puzzle. Before you ditch the selectors in favor of writing a more elaborate code to handle values, think again. Almost everything can be solved with the tools I present here. Especially when you remember that besides leveraging dynamic values of selectors in Elementor, you could:

A combination like this allowed me to create a single slider to control two different things, depending on the value of another setting

The following defines a CSS variable, and is from the Scale % slider:

'selectors' => [
    '{{WRAPPER}} .sde' => '--sde-scale-percentage: {{SIZE}};'

I use that CSS variable in two places. One is the Scale the cropped switch that simulates toggling between two selectors. The variable gets passed on to another (for use by Gap, see it later). When the switcher is on:

'selectors' => [
    '{{WRAPPER}} .sde' =>
        'transform: scale(var(--sde-scale-percentage)) scale(0.01);
        --sde-scale-pct-for-gap: var(--sde-scale-percentage);'

There is a hidden control that depends on the switch being off and contains a similar declaration but with another selector (notice it).

'condition' => [
    'scale_the_cropped!' => 'cropped'
'selectors' => [
    '{{WRAPPER}} .sde svg' =>
        'transform: scale(var(--sde-scale-percentage)) scale(0.01);'

As an extra, the Gap setting has this:

'selectors' => [
    '{{WRAPPER}} .sde' =>
            calc({{SIZE}}{{UNIT}} / (var(--sde-scale-pct-for-gap, 100) / 100)) 0;'

There you go, calc with a variable that even has a fallback! The Gap means some extra space above and below an element (with padding). But if transforms scale that, some math should counteract the adjustment. The equation doubles the gap for something that’s at half size. When not scaling the whole thing, this is not needed, hence the fallback (100/100).

Stacking transforms

You might ask, why the variable, why the double scale? I wanted to give users the usual 0-100 slider, so the value is a given. There had to be a way to divide it by 100. Initially, it looked like this:

transform: scale(calc(var(--sde-scale-percentage) / 100));

But Edge doesn’t support calc() in transforms, bummer. We get the same effect by stacking transforms of the same type, as browsers perform them sequentially.

transform: scale(var(--sde-scale-percentage)) scale(0.01);

A scale(0.01) is just a division by 100.


When developing SVG Divider for Elementor, I relied on selectors to minimize the number of settings that visibly reload or re-init the widget in the live editor. Check it out for more examples on how these work in real life, or as always, learn from Elementor’s code. For my research, I scanned about a thousand uses of ‘selectors’ in both pro and free. Furthermore, I went on their Github to grasp the more advanced pieces that they don’t even use. So, I hope this article covered everything.

Elementor’s relevant source file:

  1. elementor/core/files/css/base.php
  2. Look for the add_control_rules() method.

By now, you should be familiar with selectors in Elementor controls. If you treat this concept as a puzzle and think hard, you’ll come up with solutions that don’t require any other code. Don’t hesitate to share your thoughts and questions in the comments below!

Did you learn anything new about 'selectors'?

This site is powered by Elementor

  • This site is powered by Elementor

Related Posts

Comments are closed.

Check out Justified Image Grid, my top-selling WordPress gallery that shows photos without cropping!

Show your photos with Justified Image Grid!