While building a custom Elementor widget on a WordPress site I maintain, I needed a transform (rotate) feature that could be switched on through a popover toggle control. The logic looked straightforward: save "yes" when the toggle is on, and my CSS targeting the transform class would fire. Except the transform never fired. The element simply didn't rotate.
The symptom: the class that appeared wasn't the one I expected
I was saving the toggle value like this, assuming "yes" was the correct "on" value:
// value saved when the toggle is on
'_transform_rotate_popover' => 'yes',My CSS targeted the transform class I assumed Elementor would attach:
.e-transform {
transform: rotate( var( --e-transform-rotateZ ) );
}But when I inspected the element in the browser, the class that appeared was not .e-transform. What showed up instead was:
<div class="elementor-element e-yes">e-yes. Not e-transform. And because my CSS targeted .e-transform, the selector never matched anything → the transform never applied. The element sat still, with no error at all.
The cause: POPOVER_TOGGLE concatenates prefix_class + return_value
Here's the part that caught me out. Elementor's POPOVER_TOGGLE control builds its CSS class by concatenating two things: a prefix_class (here 'e-') with the control's return_value — not with the literal string "yes".
So the final class is prefix_class + return_value. If I save "yes", Elementor assembles 'e-' + 'yes' = e-yes. The one I wanted — e-transform — only appears if the saved value is 'transform', not 'yes'.
In other words, return_value is not a boolean on/off flag. It's a class-name fragment appended to the prefix. I was treating this toggle like a plain checkbox that stores "yes", when Elementor expected a class-name token.
The fix: save the return_value, not "yes"
The fix is to save the actual return_value token — 'transform', 'translate', 'scale', or 'skew' — so the generated class matches my CSS:
// save the return_value token, not "yes"
'_transform_rotate_popover' => 'transform',Now Elementor assembles 'e-' + 'transform' = e-transform, my .e-transform selector matches, and the rotate applies:
.e-transform {
transform: rotate( var( --e-transform-rotateZ ) );
}Valid tokens follow Elementor's transform group: transform, translate, scale, skew. Use the token that matches the class your CSS targets.
Don't forget the responsive variants and CSS regeneration
Two follow-on steps that are easy to miss:
Add the desktop base plus per-breakpoint variants. If the transform needs to differ on tablet/mobile, set the desktop base plus the _tablet and _mobile keys:
'_transform_rotate_popover' => 'transform', // desktop base
'_transform_rotate_popover_tablet' => 'transform',
'_transform_rotate_popover_mobile' => 'transform',Regenerate Elementor's CSS after writing meta directly. If you set these values via code (rather than through the editor), the cached CSS won't update on its own. Force a rebuild:
\Elementor\Core\Files\CSS\Post::create( $pid )->update();Without this step, the class may already be correct in the markup while the rendered CSS is still the old version — and you'll be chasing a bug that's actually already fixed.
The lesson
Elementor's POPOVER_TOGGLE looks like a boolean toggle, but its return_value isn't "on" — it's a class-name fragment appended to the prefix_class. Saving "yes" produces e-yes, not e-transform, so any CSS targeting the transform class never matches. Save the actual return_value token (transform, translate, scale, skew), include the responsive _tablet/_mobile variants, then regenerate the CSS via Post::create($pid)->update(). When an element refuses to react even though your CSS is "correct," inspect its class first: what's on it might not be what you think.
