Problem/Motivation
One of the ways CSS determines what property value to use when selectors have the same specificity is their source order, with later instances overriding earlier ones; this also applies to CSS files referenced via <link>
elements, where their order in the HTML document/DOM also determines which property value is used when selectors have the same specificity, with later files' values being used over earlier ones. Unfortunately, Turbo breaks this in some edge cases by simply appending the <link>
elements like so:
document.head.appendChild(element)
This is present in both the current stable version (7.3.0) and the most recent commit at the time of writing.
Steps to reproduce
This is most easily demonstrated on
the Gin admin theme β
in dark mode with widgets such as dropbuttons and vertical tabs; doing a full page load on a page that doesn't have them and then navigating to a page that does results in the Claro CSS <link>
elements to be appended to the <head>
after the Gin CSS that would normally override the Claro colours with dark mode colours, so the dark mode Gin overrides are replaced with the Claro rules of the same specificty, instead of the other way around.
Note that this primarily manifests with CSS aggregation off as aggregation preserves the correct order/weights when combining CSS files, and the observed occurrences seem to get aggregated into the same aggregate files, thus fixing this issue in a roundabout way.
Proposed resolution
We should first open an issue on the Turbo GitHub with a really simple reproducible case to get that ball rolling. Example of how we can implement a minimal reproduction in this Turbo pull request by Matt Yorkley.
If that doesn't get resolved soon enough and it turns out it's more severe for us, we can try to implement a workaround to re-order the inserted <link>
elements.
In the front-end, this can be accomplished via a MutationObserver
or (better yet) via one of the Turbo events as CSS files seem to delay triggering of the turbo:render
event while they load.
In the back-end, we would have to pass this information to the front-end, and preferably in an efficient way that doesn't require a lot of work for both PHP and the browser. We can probably just output the CSS file paths in the sorted order via the asset.resolver
service (see AssetResolver::getCssAssets()
on api.drupal.org and in GitLab).
Remaining tasks
See previous heading.
User interface changes
Stuff less borked.
API changes
None?
Data model changes
None.