Question

I am using below code to include my custom CSS file in home page only:

File: app/design/frontend/[Package]/[Theme]/Magento_Theme/layout/cms_index_index.xml

<?xml version="1.0"?>
<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>

This includes the css/home/slider.css file in the HTML but which loads before style-l.css and after style-m files included by Magento. I need my CSS file to be included after the style-m.css and style-l.css loaded by Magento.

Magento loads its style CSS files via default_head_blocks.xml which I believe if I use in my case, it will load my custom CSS style in every page which I don't want at all.

So how can achieve the desired behavior via layout updates?

EDIT

As Kishan's answer points out, adding media="all" did the trick. But I would like to know how that magic happens.

Was it helpful?

Solution

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.

  1. <css src="css/styles-m.css"/>
  2. <css src="css/styles-l.css" media="screen and (min-width: 768px)"/>
  3. <css src="css/print.css" media="print"/>
  4. <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:

  1. css/styles-m.css

  2. Magento_Theme::css/home/slider.css

  3. css/styles-l.css

  4. 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:

  1. css/styles-m.css

  2. css/styles-l.css

  3. css/print.css

  4. Magento_Theme::css/home/slider.css

OTHER TIPS

Use as below...

<css src="Magento_Theme::css/home/slider.css" media="all" />

instead of

<css src="Magento_Theme::css/home/slider.css" />
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top