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:
- Bootstrap’s utilities: https://getbootstrap.com/docs/5.3/utilities/api/
- Tailwind in DaisyUI: https://daisyui.com/docs/utilities/
- Material’s styles: https://m3.material.io/styles
- Bulma’s helpers: https://bulma.io/documentation/helpers
- PatternFly’s utility classes : https://www.patternfly.org/utility-classes/about-utility-classes
- USWDS’s utilities: https://designsystem.digital.gov/utilities/
- ...
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: 880)
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 #3517009: POC: Core styles API.
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
multipleandrequiredkey?
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.
- #3517009: POC: Core styles API
- 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
#themeand 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 contacting the maintainer of the contrib modules to ask them if they want to participate.
TODO:
- finish event subscriber for html/body
- add helper/refactor how to apply styles. Put logic into Style definition to avoid duplication between renderer and Attribute?
- item_attributes? When adding a style, add optional parameter to specify attribute key?
- ensure big_pipe compatibility? not sure if relevant that a style alters body/html attributes asynchronously after page had been rendered.
- drop short syntax for options.
- description of option.
- add lifecycle/deprecation properties on style and/or on style options
- remove item_attributes
- apply in Core: search where Core could declare style utilities with dedicated library and apply it.
- support of prefers-color-scheme or light-dark()??? No news or reaction, postponed
To test:
- no more option short syntax
- description on option
- target property is now an enum
- enums
- StyleDefinition applyOrBubbleOnAttribute
- StyleDefinition applyOnAttribute
- StyleDefinition applyOrBubbleOnArray
- Attribute object addStyle and changes
- Attribute helper changes
- bubbleablemetadata change
- Renderer changes
User interface changes
No. API Only.
Introduced terminology
"Style", "Utility", "Option"... The terminology used in this issue summary is challengeable.
API changes
No, only additions.
Change record: https://www.drupal.org/node/3586264
Need complete examples in documentation pages.
Data model changes
No.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3517033
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
pdureau commentedComment #3
catchHow does this integrate (or not) with asset loading?
I've been trying to get rid of as much of the system/base library as possible, and this has exposed various more or less undocumented 'utility classes' in core that have been around for years - clearfix, container-inline etc. that are loaded on every page despite in some cases only being used on a single or handful of admin pages in core.
Would the styles plugins also come with a .css file, or something else?
Comment #4
grimreaperHi,
@catch, this is something I have been thinking about too to avoid loading unused CSS on all pages.
- #3478113: [5.2.x] Add Bootswatch themes
- #3372792: Starterkit: split Bootstrap CSS per component
Like with UI Icons or UI Skins, adding a "library" key to a style declaration could be possible. Then is a style of this declaration is used, the library could be automatically attached.
Problem would be in some "free" areas not connected to UI Styles like WYSIWYG, or link class attributes, (same problem regarding components BTW), to detect automatically that a class is used then search for matching style and so library... This can quickly become tricky. Especially if sometimes the same CSS class is declared in multiple style declarations.
Funny that you mention "clearfix, container-inline", those classes are also in Bootstrap, and in UI Suite Bootstrap I have removed them:
And the case you mentioned, would this mean that Core should have declared libraries with one small CSS file only and properly attach only when needed?
Comment #5
catch@grimreaper I've been slowly doing that in #2880237: [meta] Refactor system/base library although mostly for larger files used in less places. However pretty much all the use cases even for these tiny utility declarations are on admin pages so overall I think we should be doing the same.
We're approaching the point where the standard profile + stark loads about 1kb of CSS on a page instead of 7kb a year ago, this could be zero if we keep going. It opens up possibilities like inlining CSS for anonymous users without massive customisation etc. But ofc the last 1kb is the hardest - although stable9 means it's not a BC break for themes using that as a base.
Comment #6
grimreaperComment #7
grimreaper@catch, Thanks! This looks very nice to go down to this few CSS by default!
Comment #8
pdureau commentedLet's start with a MR inspired from UI Styles (as a first POC, we are still open to other projects point-of-views).
Proposed scope:
#styles"universal" render property (available on any renderable with and #attributes render property)Out of scope:
preview_with,icon....)extraComment #9
grimreaperCurrent Style definition in UI Styles:
Style definition proposal:
#3469785: Add HTML class property for styles?
Need to address: #3517009: POC: Core styles API:
like in https://git.drupalcode.org/project/ui_skins/-/blob/1.1.x/src/Definition/... ?
Comment #10
pdureau commented#3517009: POC: Core styles API can be covered (and #3469785: Add HTML class property for styles? can be cancelled) by this proposal: https://www.drupal.org/project/ui_styles/issues/3517009#comment-16138133
Comment #11
pdureau commentedThe contrib modules studied in the description have been reached:
Comment #12
pdureau commentedBecause our proposal is also covering theme/mode switching, let's also support of
prefers-color-schemeorlight-dark()Comment #13
pdureau commentedComment #14
pdureau commentedComment #15
pdureau commentedSo, we are heading to a "Style API" covering both style utilities and theme/mode switching, in order to:
So, we need to think about the way of applying a style globally. What do we do? We extend
#attachedrender property with astyleskeyword? We extends our own#stylesrender property? We add an other universal render property?Let's check what is decided on #3531854: Add a Design Tokens & CSS variables API to be consistent.
Comment #16
pdureau commentedToday discussion with @grtimreaper.
YAML definition format
For style utilities
In
{provider}.utilities.yml, an example of a style utility with different options:Notes:
classis attribute default valueFor themes/modes
In
{provider}.modes.yml, the same but we have a concept of "global" style:Use in Render API
A
#stylesrender property with an associative array where:No need to extend
#attachedrender property with a styles keyword.Example:
Do we prefix plugin IDs with provider Id like SDC ?
Rendering
class="text-sm"becauseclassis the default attributefoo="text-sm"bar="text-sm"bar="text-sm"because option level winsbar="small"because we take the option's key by defaultbar="small hello"because we add both the style value and the option keyclass="baz"because we have only the option valueclass="bar baz"because we add both valuesComment #17
grimreaperIn this case it would require to put in place a "replaces" mechanism like SDC too?
Currently override (in sub themes) of plugin ID are used to disable style plugin as a whole or to add/remove options.
Should it be handled like SDC only allowing adding new options to props, new props/slots but not removing ones?
This impact the subject of value validation.
I would say let's not namespace to allow override. but when processing if a style plugin is disabled (we have not put this key in the proposition by the way..) or option is missing, ignore and log.
Comment #18
grimreaperDiscussed with @pdureau, like with Icon API and SDC.
Try to validate style definition against JSON schema.
Comment #19
grimreaperFor attribute object core/lib/Drupal/Core/Template/Attribute.php, add a "addStyle" method.
Comment #21
grimreaperNeed to take #3493495: Make style definition options translatable into account.
Comment #22
grimreaperDiscussed with @pdureau, splitting into modes.yml and utilities.yml, would require to create some additional YamlDiscovery classes to inject dynamilcally the "kind" also there is a risk of plugin ID collision and we want to avoid namespacing so let's have only one file.
TODO:
- Merge YAML files into styles.yml
- kind default to utility, can be manually set to kind.
- JSON schema: only allow utility and kind value for this property.
- Plugin manager provides method to get only plugins by a certain kind.
- Plugin manager getDefinitionsForTheme not tested yet.
- Form element
- Renderer to introduce #styles
- Attribute: add addStyle method.
Comment #23
grimreaperTODO:
-
Merge YAML files into styles.yml-
kind default to utility, can be manually set to mode.-
JSON schema: only allow utility and kind value for this property.-
Plugin manager provides method to get only plugins by a certain kind.- Plugin manager getDefinitionsForTheme not tested yet.
- Form element
- Renderer to introduce #styles
- Attribute: add addStyle method.
Comment #24
grimreaperTODO:
- Plugin manager getDefinitionsForTheme not tested yet.
- Renderer to introduce #styles
- Attribute: add addStyle method.
Comment #25
grimreaperIn UI Styles, sometimes there is "#item_attributes" instead of "#attributes" for image, responsive image.
Should we handle that too?
Comment #26
grimreaperI don't think it will be possible to add a method to attribute object because it know nothing about libraries.
So I think we will need to add a Twig function:
So that it can alter the attributes object, but I am not sure that mechanism like attach_library could be triggered:
Comment #27
grimreaperTODO:
- Plugin manager getDefinitionsForTheme not tested yet.
- Update test target enum
- Test Attribute
- Test Attribute helper
- special bubbleable/attached for html/body?
- check target for mode only.
- put logic into Style definition to avoid duplication between renderer and Attribute + tests
- item_attributes?
I don't think we're going to get around the HTML preprocess, in fact, we won't be able to.
The core has a preprocess_html in app/core/lib/Drupal/Core/Theme/ThemePreprocess.php
Except we go through it before going through app/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php, which handles attachments.
So even introducing a new type of #attached wouldn't work.
Or, we could create an event subscriber like core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
which will manipulate the response's HTML and access the response's attachments.
Comment #28
grimreaperBefore forgetting, discussion with @pdureau, for modes, light and dark is special because tight with browser and OS features of light/dark.
We should try to handle that too. So that it can prepare to provide a live theme switcher block like on design system documentation pages.
Comment #29
pdureau commentedIndeed, let's also support of
prefers-color-schemeorlight-dark()Comment #30
grimreaperutility: use attribute only
If mode: look at target.
Twig attributes: new method addStyle
- add attribute:
-- empty target: OK
-- meta:
-- html/body:
- attached library: OK
render array:
- add attribute:
-- empty target: OK
-- meta: OK
-- html/body: Ok via attached.
- attached library: OK
Preprocess callback: #styles not usable.
- add attribute:
-- empty target: ?
-- meta: ?
-- html/body: ?
- attached library: ?
new attached key: styles:
- target:
-- attribute: value
OR
- target:
-- plugin_id: plugin_option
With second option, possible during resolution to make usage of Attributes object so we can benefit from AttributeHelper::matchCollectionsTypes
UI Skins:
- preprocess HTML to set global theme: to replace with event subscriber executed before the one in Core handling html_attributes and body attributes, so able to set attachments.
- theme form settings alter: to put in UI Styles as now modes will be styles managed.
-- 2 styles form elements: one for html, one for body. Need to filter out styles by target (not just kind)
-- that way possible to handle #3485599: Handle styles on body tag
UI Styles Block:
- preprocess block: need to make it work preprocess callback
UI Styles CKE5:
- need to pass to a system of custom attributes to able to create a filter plugin that will be able to add attributes and attach library.
- on-the-fly style generation wrapped in .ck-content may be simplified to only load the library of the enabled style plugins. No more need to load all the libraries with parent theme recursively.
UI Styles Entity Status:
- hook entity view: should be unaffected or worst case, fall into same as preprocess.
UI Styles Layout Builder:
- preprocess block: need to make it work preprocess callback
- entity view alter: (to convert into hook entity view?)
- event subscriber
UI Styles Page:
- preprocess region: need to make it work preprocess callback
UI Styles UI Patterns:
- handling Attribute object: so should be ok
UI Styles Views:
- hook_preprocess_views_view: need to make it work preprocess callback
Comment #31
grimreaperDiscussed with @pdureau.
So, everything is OK:
- attributes.addStyle
- renderer / render array #styles
- preprocess callback (will alter directly $variables['#attached'] and $variables['#attributes'])
In each case:
- #attributes for kind: utility and kind: mode with empty target
- #attached html_head for kind: mode with target: meta
- #attached styles for kind: mode with target: html/body, then an event subscriber will put in html attributes or body attributes.
- #attached library for the library of the style plugin.
To add style utilities to html attributes or body attributes, use preprocess_html callback, (or force putting a style utility in #attached styles and default to body attributes).
Problem to solve is compatibility with big_pipe for #attached styles, and not sur if needed to be solved as for html and body tags big pipe should not intervene for those render element.
Also discussed about options:
- drop short syntax
- allow description per option
Comment #32
grimreaperIdea of the night.
Should we add lifecycle/deprecation properties on style and/or on style options?
Comment #33
grimreaperTODO:
- finish event subscriber for html/body
- add helper/refactor how to apply styles. Put logic into Style definition to avoid duplication between renderer and Attribute?
- drop short syntax for options.
- description of option.
- item_attributes? When adding a style, add optional parameter to specify attribute key?
- add lifecycle/deprecation properties on style and/or on style options?
- ensure big_pipe compatibility? not sure if relevant that a style alters body/html attributes asynchronously after page had been rendered.
- support of prefers-color-scheme or light-dark()
To test:
- Plugin manager getDefinitionsForTheme.
- Form element with theme.
- target property is now an enum
- Attribute object
- Attribute helper
- no more option short syntax
- description on option
- style application logic
- event subscriber
Comment #34
grimreaperI will start/continue to update contrib modules to test with the new API.
Most of the stuff remaining here are to write tests.
If some review could be done to indicate if there is a blocker or an architecture problem before writing tests on stuff that will require rework it would be nice.
Thanks!
TODO:
-
finish event subscriber for html/body-
add helper/refactor how to apply styles. Put logic into Style definition to avoid duplication between renderer and Attribute?-
item_attributes? When adding a style, add optional parameter to specify attribute key?-
ensure big_pipe compatibility? not sure if relevant that a style alters body/html attributes asynchronously after page had been rendered.-
drop short syntax for options.-
description of option.-
add lifecycle/deprecation properties on style and/or on style options- support of prefers-color-scheme or light-dark()
To test:
-
no more option short syntax-
description on option-
target property is now an enum- StyleDefinition applyOrBubbleOnAttribute
- StyleDefinition applyOnAttribute
- StyleDefinition applyOrBubbleOnArray
- Plugin manager getDefinitionsForTheme.
- Form element with theme.
- Attribute object
- Attribute helper
- style application logic
- event subscriber
- bubbleablemetadata change
Comment #35
grimreaperComment #36
johnpitcairn commentedComing in a bit late here sorry - I have been using a custom
style_optionsplugin to allow editors to apply a css property value to an element via an inline style, ie:<div style="--my-prop-name: 5">I find this has advantages over simple css utility classes or data attributes, especially for use in unitless calculations.
Will a technique like this be supported?
Comment #37
grimreaperHello,
It is in the scope (see comment 16) and already implemented in the MR.
Comment #38
johnpitcairn commentedThanks! I see it now.
The syntax for a simple range of values will be quite verbose, requiring repetition of the property name and manually specifying each individual value and unit.
As a future extension, I would like to allow for the possibility of a yaml definition something like:
A similar syntax could also be used with data attributes.
Given I had written a custom
style_optionsplugin to support this, I'd be happy enough doing the same for this API as long as the underlying support and swappability is there.Comment #39
grimreaperHello,
Thanks for your suggestion.
In short, I would say yes it will still be possible in extension as JSON schema allow additional unknown properties. But that's not the direction we want to promote.
I understand the feeling of code duplication. Know that with UI Suite Bootstrap, I went that way :D
The problem with such syntax is that it ties the style declaration to a form element and with our experience in UI Suite (UI Patterns, UI Styles, UI Skins, etc.) is that we want to decouple the declaration of the design system artifact (component, style, design token) to the UI forms (data can come from something else than a form).
1: because it is the front dev who declares the styles and how it will be applied, not how it will be configured. The style definition should know nothing (or as less as possible, maybe only suggest stuff) about Drupal Form API, where it will be configured. This would introduce drupalism into the declaration
2: having such link between style declaration and form element, would prevent (or make it harder) other contrib or custom alteration to choose something else.
3: concrete example of styles with Bootstrap:
You will regularly have some styles with options mixing number and string so no possible to get a range.
4: Form alteration is possible (I am doing it in UI Styles with a different form element extending the one provided in this MR). But style application is currently done by the style definition (or at least providing helper in it), maybe that's not the correct approach, which will check options and not other properties.
Comment #40
nod_MR looks good, a bit worried about adding more drupal specific logic to the attributes object, and more drupalism to SDC but that's a tradeoff that looks worth it
Comment #41
pdureau commentedIn my opinion, the addition of
\Drupal\Core\Template\Attribute::addStyle()is one of the key features of this MR. It is one of the 2 ways for developers to use the API with#stylerender property.Using it instead of
Attribute::addClass()orAttribute::setAttribute()will allow to:It is not a addition to SDC but also usable from any templates with an attribute object, like the SDC ones. I hope we will get rid of this object one day #3457874: HTML attributes as Twig mappings instead of PHP objects but it is another goal.
Comment #42
pdureau commentedTalked with the Mercury & Canvas teams today at Vienna.
The asked if this is related to CVA: https://cva.style
with:
note: these default values can be removed completely by setting the variant as null
Variant schema is a mapping where key is a variant ID and value is a list of style utilities classes. Example:
So, variants are like "pattern presets" (see what Canvas and Display Builder are doing) but for style utilities instead of SDC.
Like pattern presets, it would be perfect as config entities, managed by site builders by assembling style plugins.
CVA has also a Twig function :
html_cvahttps://twig.symfony.com/doc/3.x/functions/html_cva.htmlThe main purpose of this function may not be useful for SDC because we don't extends/inherits component classes.
So we will need to be able to inject a
toArray()of those config entities and use them there.Comment #43
larowlanCan we get an issue summary update here - what's the use case for this - why do we need it etc?
@grimreaper asked me to review it but I'm missing the context.
It looks like you can put #styles on any element and they bubble up with attachments and then get replaced.
I'm not super keen on the HTML rewriting - can we do that with placeholders like we do for scripts/css in \template_preprocess_html?
Comment #44
nod_re #41, sounds good. +1 to adding to the API of the attribute object
Comment #45
pdureau commentedHere is an explanation of each added/modified files (outside tests).
The API itself:
Mechanisms for local application:
Drupal\Core\Template\Attribute::addStyle()method: core/lib/Drupal/Core/Template/Attribute.phpMechanism for page wide application:
Add a new
#accept_attributesrender property to be used inElementInterface::getInfo()to know in which render element the#attributesobject can be added/modified:#item_attributesrender property to#attributes:Use the new API with
core_resizelibrary to address #2880237: [meta] Refactor system/base library so we have an use case i nCore and we can see the benfit of the API:Comment #46
pdureau commentedWe need it as we need SDC, the Icon API and #3531854: Add a Design Tokens & CSS variables API:
core_resizein this MR)So, with the visibility shared in #45, does that means Mechanism for page wide application would need some more discussion and/or work? The API is already very valuable without this mechanism, because local applcitaion is the most common use case. So we are OK to remove
core/lib/Drupal/Core/EventSubscriber/HtmlStylesResponseFilter.phpfrom the MR.Comment #47
pdureau commentedCanvas project is also expecting this feature :)
Comment #48
pdureau commentedUsages to check the validity of this proposal:
core_resizelibrary to address #2880237: [meta] Refactor system/base libraryComment #49
larowlanIn slack during discussion with @grimreaper I posted
It sounds like this bubbling isn't the main use case so I think we're in agreement there - thanks!
Comment #50
grimreaperBefore removing, I will do a test with @nod_ suggested usage of DOM. Then will remove.
And about remaining todo:
- update form element to be able to filter by target, provider, not just theme and kind.
I got an idea, I will isolate in a protected method the gathering of definitions, so that it will be easier to only override this part for contrib needs.
Comment #51
cedric_aHello, started at DrupalCon Vienna last friday, I took the time to finish my little simple test of the new API, I got 2 difficulties, due to the documentation composed from the comments above, so here are my take aways :
- the final filename is NOT
my_theme.utilities.ymlBUTmy_theme.styles.yml(thank you Florent for the clarification)- to add your styles to the render array in a preprocess, DON'T USE
$variables['#styles']['background_color']BUT$variables['#attached']['styles']['background_color']Here is my complete test implementation, the idea is to provide a setting (background color) in the theme (named 'dcwien25'), the options of this setting are loaded from the yaml file, and finally the selected options is adding its corresponding css class to the html body.
dcwien25.styles.yml
theme-settings.php
dcwien25.theme
My thoughts : this is pretty straightforward and easy to use, my next use case would be to add styling options in blocks or paragraphs (until they are replaced with Canvas !!)
Comment #52
grimreaperThanks @cedric_a for the tests.
About styles.yml it will be specified in the change record.
About #styles not working, it is because you have tested in a preprocess and potentially put #styles NOT on a render element. If you put #styles on a render element it will work:
Comment #53
grimreaperHi,
Form element updated for easier override of definitions obtention logic.
I will complete the tests during the coming days.
In the meantime if people can give reviews and feedbacks to ensure architecture.
I will appreciate to not write tests on stuff which would potentially be removed or reworked ;)
Thanks!
Comment #54
grimreaperComment #55
grimreaperComment #56
grimreaperReworked how Attribute object handle attachments to avoid side effects during rendering process and fix existing tests.
Only remaining existing test not passing is core/modules/syslog/tests/src/Kernel/SyslogTest.php
Because syslog config is null in the service during execution. I think it is due to the logger factory service added to the renderer service. So during test execution, the logger service is not recreated with the module's config imported.
But I had not the time to ensure that and figure out a fix yet.
Comment #57
grimreaperPipeline is green now!
Adding new tests.
Removing tag "Needs issue summary update" per comment 45.
Comment #58
grimreaperUpdating remaining tests to write.
To test:
-
no more option short syntax-
description on option-
target property is now an enum-
enums-
StyleDefinition applyOrBubbleOnAttribute-
StyleDefinition applyOnAttribute-
StyleDefinition applyOrBubbleOnArray- Attribute object addStyle and changes
- Attribute object addStyle through Twig
- Attribute helper changes
- style application logic
- bubbleablemetadata change
- unit or kernel or functional test on renderer change, aka apply on render array
Comment #59
grimreaperI have completed the tests:
- Attribute object
- Attribute helper
- renderer
- bubbleablemetadata
I have not found an existing test for the one line addition of HtmlResponseAttachmentsProcessor
I started to write kernel test for the renderer to see resulting render array after processing, but this was equivalent to unit tests already done.
Same for calling Attribute in Twig, with the unit tests on it PHP side I think it is ok.
Now waiting for reviews and feedbacks!
Thanks!
Comment #60
grimreaperDiscussed with @pdureau,
Changes related to removing special case of #item_attributes to use #attributes instead moved to a dedicated issue #3554447: Use #attributes instead of #item_attributes to simplify the MR of style API issue.
Comment #61
pdureau commentedThere are now 4 usages of this API.
As a style provider:
core/modules/system/system.styles.ymlin the current MTAs a style plugin consumer:
Comment #62
pdureau commentedThe MR has been simplified since comment #45. Here is the content (tests excluded).
The API itself:
Mechanisms for local application:
Drupal\Core\Template\Attribute::addStyle()method: core/lib/Drupal/Core/Template/Attribute.phpAdd a new
#accept_attributesrender property to be used inElementInterface::getInfo()to know in which render element the#attributesobject can be added/modified:Use the new API with
core_resizelibrary to address #2880237: [meta] Refactor system/base library so we have an use case in Core and we can see the benefit of the API:Comment #63
pdureau commentedComment #64
nicxvan commentedDid a really high level review, didn't review all of the test coverage.
I'm not super sure but this feels analogous to libraries and I wonder if info alter hooks should be executed somewhere: https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Comment #65
grimreaperHi,
Thanks for the reivew!
Did an update regarding small changes. Waiting for discussion approval for the remaining points.
For comment 64, I think you are right, I have not tested the hook_info_alter for styles. Looking at DefaultPluginManager:
So the alterDefinition of the stylePluginManager should be changed as???:
Comment #66
nicxvan commentedI'm honestly not sure what the established pattern is, I just know we want to alter themes after modules.
Maybe asking one of the plugin subsystem maintainers would be helpful.
Comment #67
grimreaperComment #68
grimreaperHi,
All code review threads had been fixed.
Issue still in needs review if other feedback are needed.
Comment #69
nicxvan commentedI think all of my feedback has been addressed, I don't think I can RTBC as I haven't reviewed it deep enough, but I think it's ready for that deep review.
Comment #70
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #71
grimreaperMR was not mentioning git conflict and tests are still green after rebase.
Back to needs review.
Comment #73
grimreaperHum, I am not sure how to fix pipeline after rebase.
https://git.drupalcode.org/issue/drupal-3517033/-/jobs/8345516
The "Drupal\Core\Theme\Style\StylePluginManagerInterface::getGroupedDefinitions()" method will require a new "string $label_key" argument in the next major version of its interface "Drupal\Component\Plugin\CategorizingPluginManagerInterface", not defining it is deprecated.In the latest commit I have added this argument, but CI is still not happy.
I don't get the deprecation when executing the tests locally.
Comment #74
grimreaperI forgot a method, misreading the error message.
Thanks godotislate on Slack!
https://drupal.slack.com/archives/C079NQPQUEN/p1770144037257769
Comment #75
pdureau commentedComment #76
pdureau commentedOnce the Style Utility API is here, CVA can easily being added as a config entity on the top of style plugins, in the same way:
Comment #77
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #78
grimreaperComment #79
grimreaperThe Functional JS error is unrelated.
Back to needs review after rebased.
I pushed a separated branch with the commits history before I squashed it to ease current and future rebases.
Comment #80
catchThe issue summary could use an update here, mostly an example of what the proposed API looks like.
Comment #81
grimreaperGood suggestion!
I am preparing that as a change record and also updating IS.
Comment #82
grimreaperCR created: https://www.drupal.org/node/3586264
Should documentation pages be created before merge? In case someone reads a documentation page for an API not merged yet...
Comment #83
grimreaperComment #84
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #85
kentr commentedAccessibility tag for dark mode support.
Edit: And contrast, forced-colors, or anything else related to accessibility.
Comment #86
mgiffordThere are some interesting ideas here. I'd love classes to be more than just random names.
I was thinking about this a bit last week. Not this specifically, but how do we add structure to how we define style guides. As sites get more complicated we have to start understanding why.
https://mgifford.github.io/ACCESSIBILITY.md/examples/MODERN_CSS_THEME_AR...
https://mgifford.github.io/ACCESSIBILITY.md/examples/COLOR_CONTRAST_ACCE...
Having more of an organizing logic around these styles is important.