Macros and Presets
Once you have built a few elements, the same things start repeating: the same block of
markup, the same twenty lines of CSS, the same padding-and-border control section. Macros
and presets are Element Studio's two answers — one for template code, one for controls.
Both are created from the studio's home panel (New macro, New preset) and both need
a saving location of their own type, which the plugin from
Initial Setup already registers.
Macros

A macro is a Twig macro saved as a
.twig file. Elements call it from their HTML or CSS templates
through the macros object.
Breakdance's own CSS templates are built almost entirely out of them, because the composite
design controls return structures rather than strings:
.bde-themeless-template-content-area-256146-124 {
background-color: {{ design.container.background }};
{{ macros.borders(design.container.borders) }}
{{ macros.spacing_padding_all(design.container.padding) }}
}
macros.borders(...) turns one border_complex control value into every declaration a
border needs. Writing that out by hand in each element would be miserable and inconsistent.
A macro file looks like this:
{% macro cardShell(design, selector, globalSettings, isBaseBreakpoint) %}
{{ selector }} {
max-width: {{ design.container.width.style }};
{{ _self.spacing_padding_all(design.container.padding) }}
background-color: {{ design.container.background }};
}
{% endmacro %}
Three conventions worth copying from Breakdance's macros:
Pass the selector in. Give the macro the selector it should target:
{{ macros.cardShell(design, '.bde-themeless-template-content-area-256146-124', globalSettings, isBaseBreakpoint) }}.
Pass what you need explicitly. Macros don't inherit the template's variables. Anything
the macro reads — design, globalSettings, breakpoint, isBaseBreakpoint — has to be a
parameter.
Use _self. to call sibling macros. Inside a macro file, _self.spacing_padding_all(...)
reaches other macros; macros. is only for the calling template.
Macros are best for CSS you'd otherwise copy between elements, and for markup fragments —
an icon, a button — that several elements render identically.
Preset sections
A preset section is a whole control section defined once and pulled into many elements. If
three of your elements need the same Container section with background, width and padding,
define it once as a preset rather than rebuilding it three times.
Add an Existing Preset
- Open or create an element and select Design > Add Preset.
- Choose a preset such as typography with effects and align.
- Add the element to your page. Use the preset's code help to obtain its CSS macro, then place that macro in your element's CSS template.
- Set color, size, weight, alignment, and effects on the instance. The preset supplies the controls; its macro supplies their CSS output.
Open built-in Section, Div, and Wrapper Link elements to see how reusable presets are used. Section includes presets such as simple layout and fancy background. Their code help provides the matching CSS; adding controls alone does not render their styling.
The generated file registers the section under a slug:
BreakdanceElementsPresetSectionsPresetSectionsController::getInstance()->register(
"MyBreakdanceElements\card-container",
c(
"container",
"Container",
[ /* the controls */ ],
['type' => 'section', 'sectionOptions' => ['preset' => ['slug' => 'MyBreakdanceElements\card-container']]],
false,
false,
[]
),
true, // available in Element Studio
null
);
The third argument is what makes it appear in Element Studio's Add Preset button. Set it
to false for a preset used only from code.
An element pulls one in with getPresetSection():
getPresetSection(
"EssentialElements\spacing_padding_all", // the preset's slug
"Padding", // label to show (optional)
"padding", // section slug in this element (optional)
['type' => 'popout'] // layout override (optional)
);
The label and section slug arguments matter more than they look. The label is what the user
sees, so the same preset can be "Padding" in one element and "Inner Spacing" in another. The
section slug is what the values are stored under, which means it's also the property path
your templates read — design.padding.… in the example above.
Preset sections nest, too: the accordion preset in Breakdance's own library is a section
containing controls and a getPresetSection() call for padding.
Presets bring their property-path settings with them
A preset can declare relativePropertyPathsToWhitelistInFlatProps and
relativeDynamicPropertyPaths when it registers. Those are the preset's own versions of the
settings on the Property Paths tab, expressed relative to wherever the
section ends up in the element. Every element that uses the preset inherits them
automatically, so you configure the flattening rules once instead of in each element.
Built-in spacing presets expose a spacing-bar toggle that adds the matching spacing-bar paths. Enable it and include the preset's CSS macro so the drag handles affect the rendered spacing.
Hiding a preset control in one element
Sometimes an element wants nine of a preset's ten controls. Rather than forking the preset,
open the control in Element Studio and use Hide Control From Specific Elements — the
preset stays shared, and the element that doesn't want the control names itself.
Choosing between them
| You're repeating | Use |
|---|---|
| CSS or markup in templates | A macro |
| A group of controls | A preset section |
| Both, for the same feature | Both — a preset for the controls, a macro that renders them |
That last row is how Breakdance's own design system is built: the borders preset section
collects the values, and the borders macro turns them into CSS. Splitting it that way is
what lets an element adopt half of it.