Less in Magento 2 for Front End Developers

Front end web development has change a lot since the release of Magento 1. Where once the tools and technologies you would use on a daily basis where carved in stone, now there seems to be a new tool (or framework) to learn every couple of days. The primary goal for an eCommerce platform is to generate sales for the store’s owner, which means implementing cutting edge web technology for eCommerce stores takes the backseat to performance and stability. There is little motivation on the part of a store owner (or the platform that the store is built on) to incorporate the latest JavaScript framework until it has been proven not to hurt the merchant’s conversion rates.

For this reason, if you had spent years working in Magento 1, you would be forgiven for not having used Angular JS or Sass or even jQuery. Magento 1 came packaged with Prototype JS, a framework that was the avant-garde when Magento was moving along with it 1.4 release. And for styles, CSS files ruled the land. It wasn’t until Magento 1.9 that Sass was rolled into the platform.

Magento 2 looked to change this. The tech stack is much more modern, and looks to keep up with the evolution of those tools with frequent updates. To get up to speed with these changes, I have started this series of tutorials on how Magento 2 uses Less CSS and how you can use that system to realize next level designs into a fully functional E-commerce site.

Less CSS and Magento 2

Less is a CSS preprocessor. Preprocessors are ways to optimize and modernize the tasks of writing CSS without changing the “language” itself. For those that are accustomed to writing CSS, there might be a few trick about Less that you will need to learn, but the implementation of Less is extremely intuitive. For those of you accustom to Less (or Sass for that matter), the way that Magento 2 uses Less is very unique and has plenty of custom intergeneration that you might not understand at first look. Today I hope to clear up a lot of questions and help you to leverage the power of Magento’s implementation. I’m not going to cover all the different uses of Less itself, this post (and those to come) will focus on how Magento uses Less. But just to make sure everyone is up to speed I’m going to quote the Less site:

“Less is a CSS pre-processor, meaning that it extends the CSS language, adding features that allow variables, mixins, functions and many other techniques that allow you to make CSS that is more maintainable, themeable and extendable”.

http://Lesscss.org/

What this means is that Less brings tool to the table that make the task of writing CSS modern. This gives the system a lot of control to do a lot of the optimizations, organization and debugging that just don’t exist in native CSS.

Why Less?

When Magento 2.0 shipped it came with Less baked into it’s front end themes (blank and Luma). This came as a bit of a shock to the Magento community. Before the release of Magento 2, we had all become accustom to the use of Sass that came packaged with the RWD theme of the 1.9 CE and 1.14 EE releases.

If you are the type to follow the trends of front end development, you will know that by far the adoption of Sass has outpaced Less. So the obvious question is why? I’ll give a short explanation of why, but from more reading on the subject, see Alan Kent’s blog on the issue. Magento has a long history of bringing in 3rd party systems and heavily adapting those systems to work with their software (for example their implementation of the Zend Framework). At the time when Magento 2 was getting it’s front end systems in place, Less and Sass where both getting their starts in the front-end world. For Less there was already tools in place to help Magento leverage a built in PHP compiler for the Less styles (Sass was using tools like Compass, a tool built on Ruby). This became a no brainer when it comes to choosing what system to integrate.

By having a built-in compiler for Less, Magento was able to ship a product that was completely self contained and could compile its code without any other third-party tools needed. While the decision for Magento to incorporate Less into Magento 2 is decisive, the implementation of the system in Magento 2 is unique and powerful. For more reading on the subject, see Alan Kent’s blog on the issue.

Using Less In Magento 2

To get up and running, I have written a long post about setting up a theme in Magento 2. There is no need to read that whole post just to get rolling, but if you need help with setting up a theme, that is a great place to start.

You will of course need a local development environment to do this, and I recommend Mark Shust’s Docker environment found here: https://github.com/markoshust/docker-magento. This might not be your local development system of choice, and for this blog post that shouldn’t matter. Just note that the file paths in the examples that i’m using with begin with src as the Magento install lives in a sub directory. If you are using another system, remove src or replace it with your file path to find your way.

There is a great video that walks through the set up of Docker on a mac with Magento. The project in GitHub has moved on a little since this recording, Mark is consistently updating this project, but it’s a great place to start: https://youtu.be/2VLSaceaDnM

Before we Less, CSS!

Before we start working on using Less in our theme, I wanted to show the way in which we can use just plain old CSS in our Magento Site. There is always more than one way to solve any problem, and in the case of working with Magento’s front-end, this is doubly true. Not every situation calls for the use of the Less system. Also, to fully understand the “hows” and the “whys” of the system, it’s good to have a comprehensive understanding of what the application is doing.

Create a XML file in your theme Magento_Theme/layout/default_head_blocks.xml.

Inside this file, paste the following:

// File: src/app/design/frontend/BasicTheming/theme/Magento_Theme/layout/default_head_blocks.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/framework/Module/etc/module.xsd">
    <head>
        <css src="css/example.css" />
        <css src="css/example-desktop.css" media="screen and (min-width: 768px)"/>
    </head>
</page>

This file makes the declaration in the head of the site for two CSS files example.css and example-desktop.css. We need to create both of these files in a new folder: BasicTheming/theme/web/css.

src/app/design/frontend/BasicTheming/example/web/css/example.css
src/app/design/frontend/BasicTheming/example/web/css/example-desktop.css

Clear your cache with bin/magento cache:clean and if you refresh your front end you will see no change in the styles, but when you view the page source, you will find something like shown below. Both the CSS files are being loaded into the head.

One thing to note in the picture above is the load order of the CSS files. If you are new to Magento’s use of XML, you will be forgiven for thinking that the nodes that exist in the default_head_blocks.xml file are just regular HTML nodes. These are custom declarations that often have much more complexity and nuance to them.

The <css> node when nested in the <head> node will automatically pull the file defined there into the head of the site. It will also load your file AFTER the default CSS files of styles-m.css and styles-l.css. This means that you will have CSS specificity over all the default styles of the theme(s) you are inheriting from. Furthermore, when you call out a media definition in the <css> node, it will load that file within its defined media query. This enables you to load in a certain file at certain break points which stops the load of that file on devices that don’t require them. This saves an HTTP request and data needed to pull in that file. That is why our desktop specific file is loaded last. Without even getting into Less, we are already seeing a lot of thought put into the way CSS styles on a Magento site are loaded.

Another trip to the PUB

If you take a look into the links that are created in the image above, you will see that the calls are going to the static/version1575298844/frontend folder. Digging a little deeper, if you open up the /pub/static/frontend/BasicTheming/example/en_US/css/ folder you will find that Magento has created symlinks to the CSS files in the app/design/frontend folders automatically when you reloaded the page in the browser.

This will only happen when you are in developer mode, in production mode you have explicitly recompile. Magento will scan through the XML files throughout the site’s code and grab all the CSS file declarations and create symlinks back to the CSS files in the theme (your custom theme or the default built in ones). This means you can edit the theme CSS files, refresh the front-end of the site and the last changes saved will be called into the front-end. Side note, don’t worry about the version1575298844 in the url or why you don’t see it in the folder structure. This is a deploy mode setting where Magento will sign static file. For more information, see my post on theming.

Typically, you expect most front-end code will go in the same files that will be rendered onto the page. This is not the case in Magento 2. It’s important to understand that while we will do all our work in the app/design/frontend folder, the files the browser sees are all in pub/static folder. Magento utilizes a deployment system that scans over all the development files, processes them and then deploys these processed file to a public folder that the web server looks to for delivery to the visitors of the site.

This means that all the files in the pub/static folder will be replace as we update our code. While it might be tempting to edit these files directly, these files are technically temporary files. Any changes in pub/static will be lost when you recompile. The pub/static folder is explicitly for static files, a term used for files that don’t require any additional processing by the web server. So CSS, JS and HTML.

Dynamic files (like phtml or php) are served out of the var folder. The var folder is even more tricky then the pub folder, as the files there seem like a ill tempered teenager, coming and going as they please. This folder is generated when you load any of the site’s pages. When removed, it will be rebuilt. What files are loaded from where is a little too much to cover in this post, but i want to make sure that everyone is aware of the moving parts of the system. For the phtml and php files, a lot of these come from the var folder (in 2.2 they moved the generation folder to the web root, so it’s no longer in var). We will be revisiting the role the var folder has on the site down the road.

The architecture of Magento 2 is unique. It’s extremely complex and at times frustratingly counter intuitive. Whenever you run into a system that is mind-bendingly complex, it can become easier to simply find a way to make things work without every fully understanding what it is that makes the system tick. This is a great risk when you begin working with Magento 2 as it is a system that will punish you for a lack of understanding down the line.

Enough already, lets write some styles

With my long explanation out of the way we can now open the example.css and example-desktop.css file and paste in this code:

.page-header .panel.wrapper {
    background-color: tomato !important;
}

When you refresh the front-end of your site, you should see that the header top bar is now an obnoxious red color.

Less is More

While there is nothing stopping you from designing your entire front end in CSS files by loaded them in this way, there are a lot of advantages to using the built in Less system.

If this is your first use of a CSS pre-processor, there are going to be two challenges when learning this system. The first is understanding what Less is and how to leverage its features to make better web sites and the second is Magento’s unique implementation of the Less system.

What is Less?

Returning to the quote mentioned above from the Less website:

“Less is a CSS pre-processor, meaning that it extends the CSS language, adding features that allow variables, mixins, functions and many other techniques that allow you to make CSS that is more maintainable, themeable and extendable”

All the things that makes CSS hard to manage when a project gets large can be solved with Less. Projects are typically broken down into smaller files that have a unifying concept, like having all the header styles in a _header.less file. This reduces the amount of lines you have to look through to find whatever offensive code you mistakenly wrote last week.

With Less you can create variables that can be used throughout your project, saving you from inconsistencies that can crop up. You can write utility functions (mixins) or styles that repeat on your site once and have that populate in every file. You can use style nesting to organize and clean up your sheets. You can also use Math to calculate values. Less brings CSS to the modern era, allowing front-end developers to work smarter, not harder.

How is Less?

Lets get started with some simple examples to show off both how Magento is set up to use Less and how Less is used in any project you might have.

As I said before, there is more set up involved in getting Less up and running. Unlike the CSS styles that we used earlier, the Less files have to be compile into a CSS files that is then read by the browser. Looking back to the page source that we saw earlier, you might have noticed that there were no .less files loaded. Right now there are no browsers that read .less (and I doubt there every will be). The two files you saw named styles-l.css and styles-m.css are in fact the combined Less code from every .less file in the Magento install. Magento will go through all the .less files and combine them all into one (or two, I will get to that later in the tutorial).

If you open up these files (found in pub/static/frontend/BasicTheming/theme/en_US/css/styles-m.css) you will see a monstrous file coming in around 13,000-14,000 lines long. As a developer you don’t read or write to this file, it is generated by Magento.

Let’s create a .less file to get pulled into that monster.css file. Create a file called _extend.less. Inside this file, paste this code:

// File: src/app/design/frontend/BasicTheming/theme/web/css/source/_extend.less

.page-header {
  .panel.wrapper {
    background-color: black;
  }
}

For these changes to show up, we have to take a few very important steps. First, go ahead and delete the two Css files we created earlier example.css and example-desktop.css and remove the lines in the default_head_blocks.xml. The next is to clear the cache with bin/magento cache:clean Third is the removal of the old files that are collected from all the different .less files on your site.

rm -rf src/pub/static/frontend/BasicTheming/example/en_US/css

Inside the pub/static folder you can delete all the content (with the exception of the .htaccess file found in there) and on page reload in the developer mode the files will be regenerated. But there is a trick here, if you delete this folder and refresh the page, your changes will not show back up. The files that will be rebuilt in these folders are cached. For new changes to show up, you also have to clear that.

rm -rf src/var/view_preprocessed/

But even this will not regenerate the files with the new content (well, that has now changed in 2.2. You are no longer required to run the deploy command. Refreshing the page in the front end will regenerate the files in pub/static. If you are using anything Magento 2 version before 2.2, continue with what i say below). If you do the first two steps, you will find in the pub/static folder a file (pub/static/frontend/BasicTheming/example/en_US/css/source/_extend.less) that is a match to the changes you just made. But confusingly, the CSS that is pulled into the browser will not have these changes added in. You can verify that in the file (pub/static/frontend/BasicTheming/example/en_US/css/styles-m.css). If your changes had been added in, the code would be at the bottom. To complete the process you have to run this command:

php bin/magento setup:static-content:deploy

With these three steps done, you can finally refresh the page and see the top bar of the header is now black.

You might also have noticed that we no longer are using the !important tag in our styles. We don’t need this anymore as our styles are no longer sitting next to the default styles sheets, our styles ARE the default styles sheet. And to boot, our styles are loaded into the merged CSS file last, so you will always win in the specificity cascade.

Another thing that might stand out is that the styles are nested. This is just one of the many powers of Less, you are able to nest descendant classes and ids under their parents. This makes a huge difference in organization, cleans up the code and increases the specificity of your styles. So for code like this:

.page-header {
  .panel.wrapper {
    background-color: black;
  }
}

The Less processor with parse your code and output this:

.page-header .panel.wrapper {
    background-color: black;
}

While this might seem like a small change, the full implications are huge. We have now come to an age where the readability of your code is not only possible, but also required. And by removing the need for absurdly specific styles, you have cleaned up your code, but still have the advantage of knowing your styles are effecting only the elements that you need to.

Closing thoughts

We have covered a lot of information and uncovered some key features of Less (and CSS) in Magento 2. In the next few posts we are going to go even deeper into all the features that are baked into the Less system. This will include the creation of Less mixins, organization of our files into targeted components, the use of the UI Library to make site wide changes fast and easy and the use of Grunt to make this whole process 100% easier (and faster).