2

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.

9

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

2

Use as below...

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

instead of

<css src="Magento_Theme::css/home/slider.css" />
  • Can you explain how this works please? – Ben Crook Mar 13 '18 at 10:45
  • This worked. But could you please expalin why it works as mentioned by Ben? – Rajeev K Tomy Mar 13 '18 at 10:45
  • Regarding Ben's question, I think Magento includes highest resolution or all device styles after style-l and style-m CSS files. That is the reason. – Kishan Patadia Mar 13 '18 at 10:48
  • 3
    I think Magento loads any assets with attributes after assets without. So because of the media attribute it is loaded later. Not 100% sure on that though it's something I've read on here before. – Ben Crook Mar 13 '18 at 10:54
  • 1
    @BenCrook Please have a look on my answer. I hope that clarifies your doubt – Rajeev K Tomy Mar 13 '18 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.