Turbo: CSS files merged into the <head> always appended instead of preserving full page load order, causing CSS specificity issues

Created on 5 November 2023, 8 months ago
Updated 21 January 2024, 5 months ago

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.

πŸ› Bug report
Status

Active

Version

2.0

Component

Code

Created by

πŸ‡¨πŸ‡¦Canada Ambient.Impact Toronto

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Production build 0.69.0 2024