Problem/Motivation
After SDC in Drupal 10.1 (July 2023), and the Icon API in Drupal 11.1 (Dec 2025), let’s continue to implement design systems API in Core, in order to be able to build business agnostic, shareable, Drupal themes, providing design implementations which can be leveraged by display building tools.
Styles utilities are a common artefact of design systems. Examples:
Each style is a set of mutually exclusive, self-descriptive, single-purpose, universal, CSS classes. Examples: Typography, orders, Colors, Spacing, Elevation....
Analysis of the current solutions in the contrib space
Like SDC and Icon API, we believe it must be front-dev friendly, UI logic focused, YAML plugin declaration available in Drupal theme. So, let's have a look on contrib modules are already covering this scope.
UI Styles (usage: 800)
https://www.drupal.org/project/ui_styles →
Discovery: {provider}.ui_styles.yml
in modules and themes.
Minimal example (Bootstrap 5 background color utility):
background_color:
label: Background color
options:
bg-primary: Primary
bg-secondary: Secondary
bg-success: Success
bg-danger: Danger
bg-warning: Warning
bg-info: Info
The option key is the CSS class.
With some metadata:
background_color:
label: Background color
description: Similar to the contextual text color classes, set the background of an element to any contextual class.
options:
bg-primary:
label: Primary
description: The color displayed most frequently across your app's screens and components.
bg-secondary: Secondary
bg-success: Success
bg-danger: Danger
bg-warning: Warning
bg-info: Info
Personal opinion:
I am maintainer of this module and I participated to define this format, so I may be biased, but I am confident about this format which has already been battle-tested with many design systems implementations and display building tools (layout builder views, page layout, ckeditor5, theme settings...), successfully. And the team is currently working on advanced issues like
✨
Support styles which are not HTML classes
Active
.
Style options (usage: 1700)
https://www.drupal.org/project/style_options →
More precisely, the CssClass plugin, because this module do a bit more than style utilities.
They are not really Drupal plugins, but it uses YAML discovery anyway: {provider}.style_options.yml
in modules and themes.
Example:
background_color:
plugin: css_class
label: Background color
multiple: false
required: true
default: 1
options:
- label: Primary
class: bg-primary
- label: Secondary
class: bg-secondary
- label: Success
class: bg-success
- label: Danger
class: bg-danger
- label: Warning
class: bg-warning
- label: Info
class: bg-info
Careful: default value is an integer index with this syntax.
Same example with the keyed syntax:
background_color:
plugin: css_class
label: Background color
multiple: false
required: true
default: primary
options:
primary:
label: Primary
class: bg-primary
secondary:
label: Secondary
class: bg-secondary
success:
label: Success
class: bg-success
danger:
label: Danger
class: bg-danger
warning:
label: Warning
class: bg-warning
info:
label: Info
class: bg-info
Personal opinion:
Very similar to UI styles, but with a syntax a bit more complicated. Maybe because this module do more than style utilities. Also, do we really need multiple
and required
key?
Block Style Plugins (usage: 400)
https://www.drupal.org/project/block_style_plugins →
Discovery: {provider}.blockstyle.yml in modules and themes.
Example:
colors:
label: Colors
form:
background_color:
'#type': 'select'
'#title': 'Background color'
'#options':
bg-primary: Primary
bg-secondary: Secondary
bg-success: Success
bg-danger: Danger
bg-warning: Warning
bg-info: Info
Note: The first level is a group of styles, the second level (in form
) are styles.
Personal opinion:
Maybe too complex and "drupally" for front-dev with this explicit usage of the Form API in the YAML.
Layout Builder style (usage: 23K)
https://www.drupal.org/project/layout_builder_styles →
A bit out of scope because styles are config entities instead of plugins, and because it works only with Layout Builder. But it is a popular module so let’s have a look.
There are 2 config entity types:
- One config entity by group (so by “utility”)
- And one config entity by style in the group (so by “option”)
Example (with usual config entities properties removed)
id: background_color
label: 'Background color'
multiselect: single
form_type: checkboxes
required: false
id: primary
label: Primary
classes: bg-primary
type: component
group: background_color
block_restrictions: { }
layout_restrictions: { }
id: secondary
label: Secondary
classes: bg-secondary
type: component
group: background_color
block_restrictions: { }
layout_restrictions: { }
Proposed resolution
Definition & discovery
Based on the analysis below, with some discussions:
- Required? Multiple? I am afraid we are losing the point of style utilities by introducing those.
-
✨
Support styles which are not HTML classes
Active
- Do we also add metadata for the previews in library pages like UI Styles is doing? Or do we let contrib modules do their own stuff?
- ...
In the renderer service
Once contrib or custom modules will leverage this API, they can add styles classes in $build["#attributes"]["class"]
.
This is causing a few issues:
- The syntax is verbose and error prone
- Styles classes are mixed with other classes
- There is no possibility to add checks about the existence of a style option, or the mutual exclusivity of style options.
So, it would be better to introduce #styles
universal property, which can be added to every renderables already accepting an #attributes
property:
['#type' => 'html_tag']
['#type' => 'component']
- Most of
#theme
and most of render elements
This is excluding #markup
, #plain_text
and maybe some #theme and some render elements.
So the renderer service to process this:
if (isset($elements['#styles'])) {
$elements["#attributes"] = AttributeHelper::mergeCollections(
$elements["#attributes"],
[
'class' => $elements['#styles']
]
);
unset($elements['#styles']);
}
Do we also add checks about the existence of a style option, or the mutual exclusivity of style options, here?
This is a big move to a new Render API based on design systems concept. This #styles
render property will fit well alongside ['#type' => “component”]
and [“#type” => “icon”]
renderables.
Remaining tasks
Let's start by contributing the maintainer of the contrib modules to ask them if they want to participate.
User interface changes
No
Introduced terminology
"Style", "Utility", "Option"... The terminology used in this issue summary is challengeable.
API changes
No, only additions.
Data model changes
No.