Here is the short answer:
Magento load all css assets into asset groups. Asset groups are created based on the css properties and which is what stands crucial for the rendering order of css assets.
In Details
First of all, There are lot of things happens behind the scene. So in order to make this answer abbreviated as possible, I would love to consider an example here.
Suppose I have 2 layout files as shown below:
File: vendor/magento/theme-frontend-blank/Magento_Theme/layout/default_head_blocks.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="css/styles-m.css"/>
<css src="css/styles-l.css" media="screen and (min-width: 768px)"/>
<css src="css/print.css" media="print"/>
</head>
</page>
and
File: app/design/frontend/[Package]/[Theme]/Magento_Theme/layout/cms_index_index.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Magento_Theme::css/home/slider.css" />
</head>
</page>
With that said, this is how page layout builder build()
method looks like:
Class: Magento\Framework\View\Layout\Builder
public function build()
{
if (!$this->isBuilt) {
$this->isBuilt = true;
$this->loadLayoutUpdates();
$this->generateLayoutXml();
$this->generateLayoutBlocks();
}
return $this->layout;
}
Builder::loadLayoutUpdates()
reads defaul_head_blocks.xml
first and then reads cms_index_index.xml
. As a result, the css assets which will be considered in the following order.
<css src="css/styles-m.css"/>
<css src="css/styles-l.css" media="screen and (min-width: 768px)"/>
<css src="css/print.css" media="print"/>
<css src="Magento_Theme::css/home/slider.css" />
This data will be provided to the HEAD config generator class (Magento\Framework\View\Page\Config\Generator\Head
) and in the process()
method all the HEAD assets (This include js, css and meta etc.) will go through different processes such as adding assets, removing assets, processing meta data etc.
Addition of assets into the structure happens in Magento\Framework\View\Page\Config\Generator\Head::processAssets()
protected function processAssets(Structure $pageStructure)
{
foreach ($pageStructure->getAssets() as $name => $data) {
if (isset($data['src_type']) && in_array($data['src_type'], $this->remoteAssetTypes)) {
if ($data['src_type'] === self::SRC_TYPE_CONTROLLER) {
$data['src'] = $this->url->getUrl($data['src']);
}
$this->pageConfig->addRemotePageAsset(
$data['src'],
isset($data['content_type']) ? $data['content_type'] : self::VIRTUAL_CONTENT_TYPE_LINK,
$this->getAssetProperties($data),
$name
);
} else {
$this->pageConfig->addPageAsset($name, $this->getAssetProperties($data));
}
}
return $this;
}
Here both $this->pageConfig->addPageAsset()
and this->pageConfig->addRemotePageAsset()
will create asset groups based on the asset property and then append the assets to any one of the asset group based on its property..
An asset property is an an array of attribute values other than src
attribute.
For example, in the case of the asset <css src="css/print.css" media="print"/>
, the asset property will be ["media" => "print"]
.
Each asset group is an instance of Magento\Framework\View\Asset\GroupedCollection
and the method Magento\Framework\View\Asset\GroupedCollection::add()
is what creates the asset group creation (if needed) and assignation of assets into the groups.
So for the above scenario, 3 groups will be created.
1. Asset Group ([]
) Holds:
css/styles-m.css
Magento_Theme::css/home/slider.css
2. Asset Group (["media" => "screen and (min-width: 768px)"]
) Holds:
css/styles-l.css
3. Asset Group (["media" => "print"]
) Holds:
css/print.css
As you can see above since the asset property of the asset Magento_Theme::css/home/slider.css
and css/styles-m.css
are the same (ie an empty array), both of them will be grouped together. Since this the first group asset which is created, all the assets in that group asset will be rendered first.
So in the above case, the asset rendering order will be:
css/styles-m.css
Magento_Theme::css/home/slider.css
css/styles-l.css
css/print.css
Another Example
Now if our css are configured in the below order:
<css src="css/styles-m.css"/>
<css src="css/styles-l.css" media="screen and (min-width: 768px)"/>
<css src="css/print.css" media="print"/>
<css src="Magento_Theme::css/home/slider.css" media="all" />
Then the asset grouping will be like this:
1. Asset Group ([]
) Holds:
css/styles-m.css
2. Asset Group (["media" => "screen and (min-width: 768px)"]
) Holds:
css/styles-l.css
3. Asset Group (["media" => "print"]
) Holds:
css/print.css
4. Asset Group (["media" => "all"]
) Holds:
Magento_Theme::css/home/slider.css
and hence the css rendering order will be:
css/styles-m.css
css/styles-l.css
css/print.css
Magento_Theme::css/home/slider.css