Magento 2 and the Less CSS Preprocessor

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Last week we covered RequireJS, which is the foundational framework for Magento 2’s modern approach to javascript. Today we’re going to do the same for Magento 2’s use of cascading style sheets.

Web development, whether delivering marketing pages or software applications, has always struggled with the browser compatibility problem. On one hand, it’s a fantastic testament to the world wide web that completely different companies can all produce the different browsers engines that have, more or less, a shared set of functionality.

The devil, of course, is in that more or less.

Slightly different interpretations of standards, or lingering older versions of browsers, can wry havoc with developers trying to get consistent browser behavior. This is especially true for people who don’t have the time (or company culture) to perform full cross browser tests of every feature. The advent of the mobile web has made this problem even worse, as mobile device browsers are almost, but not quite entirely, compatible with their desktop counterparts.

Javascript, in some respects, has it easier than CSS. Thanks to some excellent early reverse engineering by Microsoft’s JScript engineers and the development of a reasonable standardization process, the core javascript language performs identically between various browser implementations. The APIs javascript calls into (i.e. the DOM) are another story, but since javascript is a programming language it’s relatively easy for programmers to create shim libraries (like jQuery) to carve out a consistent portion of the DOM to work with.

On the Cascading Style Sheets (CSS) side of things, and back in the quiet days of web development when IE stagnated at version 6 and Firefox/Safari were quietly catching up, “web standards” proponents attempted to carve out a set of CSS rules that worked across browsers, and a set of CSS techniques that relied on non-obvious-but-standard CSS behavior to achieve desired layout behaviors.

Then Microsoft woke up with IE 7/8/9/10/11/edge, Apple essentially created the first real mobile web with Safari for iPhone (while simultaneously pushing closed non-HTML based applications for accessing web data), Firefox started pushing its own standards agenda, Google started turning Chrome into a platform, and a weird “HTML5” movement started. The only WaSPs left in NYC were in The Hamptons. CSS developers were once again faced with death by a thousand browser cuts. While CSS is “code”, and an important valuable skill to master, CSS is not a programming language. The CSS system cannot change its own behavior via a turing complete programming language.

All this led to the rise of CSS pre-processors. A CSS pre-processor lets a front end developer write their styling rules in a custom style language, and then have a stand alone computer program (or “compiler”) turn that custom style language into CSS files that are compatible with multiple browsers.

With pre-processors, the maintainer of the pre-processor (or the pre-processor’s library) becomes the CSS expert, and the designer is freed from needing to remember which CSS rules are safe, which -prefix classes to add for each browser, which crazy CSS standards-bugs are used to implement common UI patterns, etc.

Understanding Less

Magento 2 uses a CSS pre-processor called Less. With Less, designers write their styles in a CSS like language. This is a sample of a Less stylesheet from the Less documentation.

#File: example.less
@base: #f938ab;

.box-shadow(@style, @c) when (iscolor(@c)) {
  -webkit-box-shadow: @style @c;
  box-shadow:         @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
  .box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
  color: saturate(@base, 5%);
  border-color: lighten(@base, 30%);
  div { .box-shadow(0 0 5px, 30%) }
}

As you can see, this sort of looks like CSS, but seems to have things like functions and variables available. Designers will write Less based code, and then compile it with a program like lessc, which will convert it to CSS.

$ lessc example.less 
.box {
  color: #fe33ac;
  border-color: #fdcdea;
}
.box div {
  -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

As you can see above, variables have been expanded, functions like lighten have been applied to colors, etc. Less has a reputation as a more “programmer-y” pre-processor, and it’s not surprising that Magento’s engineering team picked it over other popular choices like Sass.

Covering all of Less’s functionality would take a book, and my days of slinging background-color are long over. Instead, we’re going to focus on how Magento has incorporated Less into their front end framework code. Whether you plan on embracing Less, finding ways to work with other CSS tools, or leave the styling to designers, understanding how Magento processes and compiles Less code is an important skill for any working Magento 2 developer to master.

Getting Started with Magento and Less

We’re going to start by creating a module named Pulsestorm_StartingWithLess, and then we’ll use this module to add a CSS file to every Magento Page. We’re going to start assuming you’re running Magento in developer mode. If you’re not sure what that means, you’ll want to review our articles so far.

You can create the base module files using the pestle command line framework’s generate_module command.

$ pestle.phar generate_module Pulsestorm StartingWithLess 0.0.1

and then enable the module in Magento by running the following two commands

$ php bin/magento module:enable Pulsestorm_StartingWithLess
$ php bin/magento setup:upgrade    

If you’re interested in creating a module by hand, or curious what the above pestle command is actually doing, take a look at our Introduction to Magento 2 — No More MVC article.

Once created, add the following default layout handle file to the frontend area.

#File: app/code/Pulsestorm/StartingWithLess/view/frontend/layout/default.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Pulsestorm_StartingWithLess::red-alert.css"/>
    </head>
</page>

This is a page layout handle XML file, which means it understands the special head and body sections. The default handle fires on every page request. The contents of the head tag adds the CSS file red-alert.css to the page. Next, add the following red-alert.css file to your module.

#File: app/code/Pulsestorm/StartingWithLess/view/frontend/web/red-alert.css
body
{
    background-color:#f00;
}    

With the above in place, clear your Magento cache, reload the Magento home page, and you should now see an obnoxious red background, and CSS link tag in your document’s source.

<link  rel="stylesheet" type="text/css"  media="all" href="http://magento.example.com/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css" />

Your <link/> tag’s href attribute may look a little different depending on the theme you’re using. This article assume you’re using the luma theme that ships with Magento 2.

If any of the above concepts confused you, you may want to review the articles in our series so far.

Your First Less File

Next up, we’re going to add our first Less file. The following assumes you’re running in developer mode, and that the option at

System -> Stores -> Configuration -> Advanced -> Developer -> Front-end Developer Workflow 

is set to Server Side Less Compilation.

First, remove the red-alert.css file from your module.

rm app/code/Pulsestorm/StartingWithLess/view/frontend/web/red-alert.css 

Confirm you’ve removed it by reloading the home page and noting the non-garish white background is restored. We’ll also want to remove the symlink Magento created in pub

$ find pub/static -name red-alert.css
pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css     

$ rm pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css     

Next, create the following Less source file.

/* File: app/code/Pulsestorm/StartingWithLess/view/frontend/web/red-alert.less */
@oh-my-blue: #0000ff;
body
{
    background-color:@oh-my-blue;
}      

Notice we’ve put this file in the same location as our CSS file, and given it the same base name. The only difference is the file extension (.less instead of .css). The contents of the file are also slightly different — we’ve used a Less variable (@oh-my-blue) to add a blue background.

With the above in place, clear your cache and reload the homepage. You should now see a garish blue background. Congratulations! You just created your first Less source file.

Understanding Magento’s Less Implementation

If you view the source of your home page, you may be surprised to find your CSS <link/> tag is still in place, and still pointing to red-alert.css

<link  rel="stylesheet" type="text/css"  media="all" href="http://magento.example.com/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css" />

In one way, this makes sense. After all, our default.xml still points to this file. But how is Magento loading red-alert.css file if we deleted this file?

The missing piece of information is understanding Magento’s Less processing. When you add a css file via <css/> tag

<css src="Pulsestorm_StartingWithLess::red-alert.css"/>

Magento will search for this file on disk. If Magento doesn’t find it, Magento will look again, but instead of looking for a .css file, Magento will look for a .less file with the same name (red-alert.less). If Magento finds the Less source file, Magento will transform the file into CSS, and generate a CSS file. If you load the above CSS in a browser, you’ll see Magento has transformed the Less file into a CSS file.

#File: http://magento.example.com/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css
body {
  background-color: #0000ff;
}

If you look for the file in pub, you’ll see it’s a real file, and not a symlink that points to a module file.

$ find pub/static -name red-alert.css
pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css

$ ls -l pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css
-rw-rw-rw-  1 _www  staff  38 Feb  4 16:29 pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css

Remove this generated file, and reload the CSS URL. You’ll see Magento simply regenerates the file for you.

Understanding Less.js

One problem with the above system is that, once a file’s generated, Magento won’t recheck the Less file for any changes. That is, if you changed the Less file to create a red background

/* File: app/code/Pulsestorm/StartingWithLess/view/frontend/web/red-alert.less */
@oh-my-red: #ff0000;
body
{
    background-color:@oh-my-red;
} 

Magento wouldn’t see these changes. Since there’s a red-alert.css file on disk already, Magento will just serve that file. In order to pick up these changes, you need to remove the generated CSS file.

rm pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css

You’ll also need to clear Magento’s view_preprocessed folder. This is a cache folder for Less source files

rm -rf var/view_preprocessed/*

While some programmers may be used to the “edit file, compile, view results” workflow, CSS developers are used to editing a file and seeing the results immediately. One solution for this is something called less.js.

The intended use of less.js is

  1. You include less.js on your HTML page
  2. You serve Less source files directly to the browser
  3. The less.js library transforms the Less source into CSS, and applies the CSS to the page

In other words, less.js simulates what it would be like if Less were supported natively in your browser. You can turn on less.js by navigating to the following system configuration section

System -> Stores -> Configuration -> Advanced -> Developer -> Front-end Developer Workflow 

and setting the Front-end Developer Workflow option to Client side less compilation. With this setting enabled (and if you remove any generated files/links from pub/static), changes to Less source files will be immediately reflected on the next page load.

Behind the scenes, when you have less.js enabled, Magento does three things. First, Magento adds the following two javascript files to your page’s source.

<script src="http://magento.example.com/static/adminhtml/Magento/backend/en_US/less/config.less.js"></script>
<script src="http://magento.example.com/static/adminhtml/Magento/backend/en_US/less/less.min.js"></script>

The less.min.js file is the minified less.js source. The config.less.js file is a javascript configuration file used to configure less.js. Having these here enables client side Less compilation.

The second change is each CSS link will have its rel attribute’s value changed from stylesheet

<link  rel="stylesheet" type="text/css" ... />

to stylesheet/less.

<link  rel="stylesheet/less" type="text/css" ... />

This identifies the source of a <link/> as needing the Less transformations provided by less.js.

Finally, and most importantly, when you’re running Magento in developer mode with client side Less compilation on, Magento will serve Less source files instead of CSS files. i.e., if you try to download the red-alert.css file in your system

http://magento.example.com/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css

instead of getting the CSS file, you’ll get the Less source file

@oh-my-blue: #ff0000;
body
{
    background-color:@oh-my-blue;
} 

While this client-side processing is useful, I’d be wary of relying too heavily on it. The GA release contains what seems to be a bug where Magento can serve CSS source files with a rel="stylesheet/less". Processing CSS as Less can create subtle bugs. Also, as we learned last time, Magento uses RequireJS as its foundational javascript framework. RequireJS means there’s DOM altering javascript loading asynchronously in the background, which may interfere with the DOM altering less.js.

Less and Production Mode

Both server side and client side Less compilation are developer mode only features. Let’s try changing Magento into production mode and observe how the system behavior changes.

Switch your system into production mode

#File: .htaccess
SetEnv MAGE_MODE production

and remove the generated red-alert.css file.

rm pub/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css    

Now, try reloading your red-alert.css URL. You’ll end up seeing a 404 Not Found error.

curl -I 'http://magento.example.com/static/frontend/Magento/luma/en_US/Pulsestorm_StartingWithLess/red-alert.css'
HTTP/1.1 404 Not Found

That’s because, in production mode, Magento will not automatically convert your Less source files into CSS, and will not perform client side Less actions. Similar to PHP code generation, this is for performance and security reasons. Instead, Magento will transform your files once, when you run setup:static-content:deploy.

$ php bin/magento setup:static-content:deploy
Requested languages: en_US

=== frontend -> Magento/blank -> en_US ===
............................................................................................................................................................................

After running the above command, each theme in the system will have a CSS file generated if there’s a CSS file configured in a <head/> section with a corresponding Less source file.

Wrap Up

That’s the foundations of Magento 2’s Less implementation. If you’re interested in learning more, the Cascading Style Sheets section of the dev docs site has some good, if scattered, information on styling Magento 2.

I also know Magento has some built-in support for grunt, and there’s talk of unofficial-ish gulp support. My CSS friends tell me grunt and gulp are build tools that can help work around the limitations of solutions like less.js, but I’m just a frozen XHTML 1.0 transition caveman in these matters.

Regardless of how you’re compiling your Less file, or generating your own CSS outside of Magento’s systems, understanding how Magento’s framework code handles generating its own CSS via Less will be a vital skill when working with Magento 2 systems.

Originally published February 6, 2016

Magento 2 and RequireJS

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Now that we’ve covered the basics of including javascript and CSS files in Magento 2, we’re going to start exploring Magento’s adoption of modern front end tools and libraries.

Magento’s in a bit of a tricky position when it comes to adopting modern front end technologies. Magento is a software platform, and an ecommerce one at that. Unlike an agency, whose marketing project will be discarded 6 months after launch or whose prototyping work will be depreciated a year after the product launches, an ecommerce software platform needs to focus on stable, proven technologies that are going to span the test of time.

Today we’re going to focus on the library that underlies nearly every javascript feature built in Magento 2 — RequireJS.

Before we get to Magento’s RequireJS implementation, we’re going to take a whirlwind tour of what RequireJS does.

RequireJS

RequireJS is a javascript module system. It implements the Asynchronous Module Definition (AMD) standard for javascript modules. In the terms of AMD, a javascript module provides a way to

  1. Run a javascript program that doesn’t default to the global namespace
  2. Share javascript code and data between named modules and programs

That’s all RequireJS does. You may use a RequireJS module that implements some special bit of functionality, but its not RequireJS that provides that functionality. RequireJS is the pneumatic tube that ensures the functionality is delivered to you.

The RequireJS start page has a good example of how RequireJS works. We’re going to crib from it and add some extra explanations.

First, download the RequireJS source and save it to a folder named scripts.

Then, create the following file.

<!-- File: require-example.html -->
<!DOCTYPE html>
<html>
    <head>
        <title>My Sample Project</title>
        <!-- data-main attribute tells require.js to load
             scripts/main.js after require.js loads. -->
        <script data-main="scripts/main" src="scripts/require.js"></script>
    </head>
    <body>
        <h1>My Sample Project</h1>
    </body>
</html>

As you can see, this page loads in the main RequireJS with the following

<!-- File: require-example.html -->    
<script data-main="scripts/main" src="scripts/require.js"></script>

In addition to the standard src attribute, there’s also the custom data-main attribute. This tells RequireJS that it should use the scripts/main module as the program’s main entry point. In our case that’s scripts/main, which corresponds to a file at scripts/main.js.

Create the following file.

//File: scripts/main.js
requirejs([], function() {
    alert("Hello World");
});

With the above created, load your HTML page in a browser. You should see the Hello World alert. Congratulations! You just created your first RequireJS program.

By itself, RequireJS hasn’t done much that jQuery’s document ready functions can’t accomplish

jQuery(function(){
    alert("Hello World");
});

Where RequireJS sets itself apart is in its module system. For example, if we wanted to use a hypothetical module named helper/world, we’d change our main.js file to match the following.

requirejs(['helper/world'], function(helper_world) {
    var message = helper_world.getMessage();
    alert(message);
});

That is, we specify the modules we want to load as an array, and pass that array as the first argument to the requirejs function call. Then, RequireJS passes the single object the helper/world module exports to our main function as the first helper_world parameter.

Of course, if you ran the above you’d get a javascript error. That’s because we need to define our helper/world module. To define a module, turn the module name into a file path, and add the following contents

//File: scripts/helper/world.js
define([], function(){
    var o = {};
    o.getMessage = function()
    {
        return 'Hello Module World';
    }
    return o;
});

A module definition is very similar to our main program definition. The main difference is the use of the define function instead of the requirejs function. The first parameter of define is a list of RequireJS modules you’d like to use in your module (in our case, this is an empty array — in the real world most modules will use other modules). The second parameter is the javascript function/closure that defines what your module will return.

RequireJS has no opinion on what a javascript module should return/export. It could return a plain string. It could return a simple javascript object with a single method defined (as we have above). It could also load in a javascript library like PrototypeJS and return a PrototypeJS object. The only thing RequireJS does is provide a system for sharing javascript code via modules — the rest is up to each individual project developer.

Before we get to Magento’s RequireJS implementation, there’s two additional RequireJS topics we’ll need to cover: Require JS file loading, and RequireJS module naming.

RequireJS File Loading

By default, RequireJS will convert a module name like helper/world into an HTTP(S) path like the following

http://example.com/scripts/helper/world.js
https://example.com/scripts/helper/world.js
//example.com/helper/scripts/world.js

That is, the module name is turned into a file path, with the last segment being a file name that ends in js. By default, RequireJS will use the folder where the require.js script is located as its base (/scripts in the above example).

However, RequireJS allows you to set a different base path for your scripts. Before the start of your RequireJS program, include the the following code.

require.config({
    baseUrl: '/my-javascript-code',
});

With the above in place, when RequireJS needs to load the helper/world module, it will load it from

http://example.com/my-javascript-code/helper/world.js
https://example.com/my-javascript-code/helper/world.js    
//example.com/my-javascript-code/helper/world.js

This feature allows you to store your javascript files wherever you want in a RequireJS based system.

RequireJS: Module Naming

So far in our examples, a RequireJS module name has been tied to the location of that module’s source on disk. In other words, the helper/world module is always going to be located at the path helper/world.js.

RequireJS allows you to change this via configuration. If, for example, you wanted your helper/world module to be named hello, you’d run the following configuration code somewhere before the start of your program

require.config({   
    paths: {        
        "hello": "helper/world"
    },
});

The paths configuration key is where we can rename/alias modules. The key of a paths object is the name you want (hello), and the value is the module’s actual name (helper/world).

With the above configuration in place, the following program

requirejs(['hello'], function(hello) {
    alert("Hello World");
});

Would load the hello module from the helper/world.js path.

There are many other configuration directives that control how and where RequireJS will load javascript modules. While outside the scope of this article, the entire “Load Javascript Files” section of the RequireJS API Documentation is worth reading.

If you take away one thing from this brief RequireJS introduction tutorial, it should be that the point of RequireJS is for day-to-day javascript development should not need to concern itself with how a module load over HTTP. As a javascript developer, you should be able to say “I want to use the methods in module X to do something”, and just be able to use module X. The only time you should need to worry about file loading is when you’re adding a new module to the system, adding some non-RequireJS/AMD compatible code to the system, or looking for a module’s source files so you can figure out what it does.

Magento 2 and RequireJS

This brings us to Magento’s RequireJS implementation. Magento pulls in the main RequireJS library for you, includes some additional configuration, and provides a mechanism that will let you add your own additional RequireJS configurations.

The first thing you’ll want to make note of is Magento 2’s use of the aforementioned baseUrl feature of RequireJS. If you view the source of a Magento page, you’ll see something like the following

<script type="text/javascript">
    require.config(
        {"baseUrl":"http://magento.example.com/static/adminhtml/Magento/backend/en_US"}
    );
</script>

With the above configuration, this means when Magento 2 encounters a RequireJS module named helper/world, it will load that module source from a URL that looks something like this

http://magento.example.com/static/adminhtml/Magento/backend/en_US/helper/world.js

If you’ve worked through the previous articles in this series, you may recognize that URL as the “loading a front end static asset from a Magento module” URL. This means you could place a RequireJS module definition file in your module at

app/code/Package/Module/view/base/web/my_module.js

and it would be automatically available as a RequireJS module named

Package_Module/my_module

loaded via the following URL

http://magento.example.com/static/adminhtml/Magento/backend/en_US/Package_Module/my_module.js

It also means you could immediately start writing requirejs programs in your phtml templates by using code like the following

<script type="text/javascript">
    requirejs('Package_Module/my_module', function(my_module){
        //...program here...
    });
</script>

Or by adding stand-alone javascript files that do the same.

Configuring RequireJS via Modules

Earlier in our tutorial, we covered two RequireJS configuration directives — baseUrl and path. There are plenty of other RequireJS configuration directives, and as you get into advanced use of the framework (or you’re dealing with Magento core code that’s gotten into advanced use) you’ll find you need to use them.

Every Magento module has the ability to add RequireJS configuration directives via a special view file named requirejs-config.js.

app/code/Package/Module/view/base/requirejs-config.js    
app/code/Package/Module/view/frontend/requirejs-config.js    
app/code/Package/Module/view/adminhtml/requirejs-config.js    

This is a special javascript file that Magento will automatically load on every page load using the area hierarchy. We’re going to give it a try. First, we’ll need to create a module named Pulsestorm_RequireJsTutorial. You can create the base module files using the pestle command line framework with the following command

$ pestle.phar generate_module Pulsestorm RequireJsTutorial 0.0.1

and then enable the module in Magento by running the following two commands

$ php bin/magento module:enable Pulsestorm_RequireJsTutorial
$ php bin/magento setup:upgrade    

If you’re interested in creating a module by hand, or curious what the above pestle command is actually doing, checkout our Introduction to Magento 2 — No More MVC article.

Regardless of how you’ve created it, once you have a module created and enabled, add the following file

//File: app/code/Pulsestorm/RequireJsTutorial/view/base/requirejs-config.js
alert("Hello");

Clear your cache, and load any page in your Magento system. You should see your alert function call. Congratulations — you just added a requirejs-config.js file to your Magento module.

The Purpose of requirejs-config.js

While you can use requirejs-config.js to run any arbitrary javascript, its main job is to

  1. Allow end-user-programmers to add require.config options to Magento’s RequireJS system
  2. Allow end-user-programmers to perform any other setup/configuration their javascript needs

To understand how RequireJS does this, we need to look at where Magento actually pulls in these requirejs-config.js files. If you take a look at any source page in your Magento installation, you should see a tag that looks like this

<script  type="text/javascript"  src="http://magento.example.com/static/_requirejs/adminhtml/Magento/backend/en_US/requirejs-config.js"></script>

This is a special javascript file that Magento generates during setup:di:compile (in production mode) or on the fly (developer and default mode). If you’re unfamiliar with how Magento’s various modes affect front end asset serving, checkout our Magento 2: Serving Frontend Files article. We’re going to assume you’re running in developer mode for the remainder of this article.

If you take a look at the source of this file in a browser, you’ll see your alert statement in a code block that looks something like this

(function() {
    alert("Hello World");
    require.config(config);
})();

While it’s not 100% obvious, by generating this javascript code block from requirejs-config.js, Magento 2 is letting us add extra RequireJS initializations to the system.

This may make more sense with a concrete example. Let’s replace our requirejs-config.js with the following

var config = {
    paths:{
        "my_module":"Package_Module/my_module"
    }
};

alert("Done");    

What we’ve done here is define a javascript variable named config, and changed our alert value. If you go back and reload requirejs-config.js it might be clearer now what Magento is doing.

(function() {
var config = {
    paths:{
        "my_module":"Package_Module/my_module"
    }
};

alert("Done");
require.config(config);
})();

For every individual requirejs-config.js, Magento will create a chunk of code that looks like this

(function() {
    //CONTENTS HERE
    require.config(config);
})();

but with //CONTENTS HERE replaced by the contents of requirejs-config.js.

var config = {
    paths:{
        "my_module":"Package_Module/my_module"
    }
};

alert("Done");
require.config(config);

This means if we define a config variable in our requirejs-config.js file, Magento will ultimately pass it to require.config. This will let any Magento module developer use RequireJS features like shim, paths, baseUrl, map, or one of the many others from RequireJS’s configuration directives.

Understanding Lazy Loading

Another important thing to understand about RequireJS is that modules are lazy loaded. RequireJS will not load any javascript module source file until someone users that javascript module as a dependency.

In other words, if we used the configuration

var config = {
    paths:{
        "my_module":"Package_Module/my_module"
    }
};

Magento will not load the Package_Module/my_module.js file by default. Magento will only load that file once you’ve used it as a module


requirejs(['my_module'], function(my_module){

});

requirejs(['Package_Module/my_module'], function(my_module){

});    

define(['Package_Module/my_module'], function(my_module){

});        

Remember, the point of RequireJS is that day-to-day javascript developers shouldn’t need to worry about how their programs make HTTP requests for their source files. Lazy loading is an implementation detail that, in ideal circumstances, saves the end user the bandwidth of needing to download source files that a particular page might not need.

However, in less than ideal circumstance, this lazy loading behavior can make it tricky to work with older javascript frameworks and libraries. We’ll discuss one example of this below when we talk about a few jQuery gotchas.

Global jQuery Object

Even if you decide RequireJS is not for you, and you want to stick to plain old jQuery in Magento 2, you’ll still need to be aware of how RequireJS interacts with libraries that predate the AMD standard.

In Magento 2, jQuery is loaded as a RequireJS module. This means if you attempts to use code like the following

<script type="text/javascript">
    jQuery(function(){
        //your code here
    });
</script>

Your browser will complain that jQuery is undefined. That’s because the global jQuery object won’t be initialized until you use jQuery as a RequireJS module. If you’re used to writing code like the above, you’ll need to

  1. Replace it with code that kicks off execution of a RequireJS program
  2. Configure that program to use the jquery module as a dependency

In other words, something like this

requirejs(['jquery'], function(jQuery){
    jQuery(function(){
        //your code here
    });
});

To review — you kick off execution of a RequireJS program by calling the requirejs function and passing it a list of module dependencies, and an anonymous javascript function that will act as your programs main entry point.

The list of module dependencies is the first argument to requirejs — i.e. the following code says “My Program is dependent on the jquery module”

requirejs(['jquery'],    

The anonymous function that acts as your program’s main entry point is the second argument to the requirejs function

requirejs(['jquery'], function(jQuery){
    //...
});

RequireJS will call this function for you. For each dependency you configure RequireJS will load the module and pass in its returned (or, in RequireJS speak, exported) module.

Modern versions of jQuery will detect if they’re being included in a RequireJS/AMD environment and define a module that returns the global jQuery object.

// File: http://code.jquery.com/jquery-1.12.0.js
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

if ( typeof define === "function" && define.amd ) {
    define( "jquery", [], function() {
        return jQuery;
    } );
}    

RequireJS and jQuery Plugins

There’s another gotcha to using jQuery and RequireJS together. The jQuery library, which predates RequireJS and the AMD standard by many years, developed its own plugin system. While not module based, this system plays relatively nice with javascript’s global by default environment — plugin developers create their plugins by modifying the single global jQuery object.

This presents a problem for RequireJS — as we mentioned above, the global jQuery object is not defined until the jquery module is used in a requirejs program, and until RequireJS calls that program’s main entry-function. This means the long standing way of including a jQuery plugin

<script src="http://magento.example.com/js/path/to/jquery/plugin/jquery.cookie.js">

will fail when the plugin attempts to use the global jQuery object and/or $ alias.

//File: http://magento.example.com/js/path/to/jquery/plugin/jquery.cookie.js
var config = $.cookie = function (key, value, options) {

If you want to include a jQuery plugin for use in Magento 2, you’ll need to do it via RequireJS. Fortunately, the process is relatively straight forward.

First, you’ll want to create an alias for the plugin path using the paths configuration property.

var config = {
    paths:{
        "jquery.cookie":"Package_Module/path/to/jquery.cookie.min"
    }
};

The above configuration creates a module named jquery.cookie that points to the jQuery cookie plugin source file in the Package_Module module.

At this point, you may think its OK to start using jQuery with your plugin by doing something like this

requirejs(['jquery','jquery.cookie'], function(jQuery, jQueryCookie){
    //my code here
});

After all, listing both jquery and jquery.cookie as dependencies should trigger a loading of both files.

You’d be right — but only some of the time. RequireJS loads its module source files asynchronously, and doesn’t promise anything about the order they’re loaded in. That means the above code may load the core jQuery library first. However, depending on other scripts running on the page and the network, it may also load the jQuery cookie plugin first. If this happens, the cookie plugin will load without a global jQuery object. Without a global jQuery object, the plugin initialization will fail.

This may seem like a poor design decision — but you need to remember that RequireJS, and the AMD standard, were designed to stop the use of global state. It’s not surprising that RequireJS doesn’t work seamlessly with a library like jQuery. Even though jQuery is responsible about its use of global state (one global jQuery object), it still uses global state, and RequireJS isn’t going to get in the business of deciding who does and doesn’t use global state responsibly.

Fortunately, there is a solution. The RequireJS shim configuration directive allows you to configure what I’ll call “load order” dependencies. i.e., you can say

Hey RequireJS — when you load the jquery.cookie module? Make sure you’ve completely loaded the jquery module first.

Configuration wise, this looks like

var config = {
    paths:{
        "jquery.cookie":"Package_Module/path/to/jquery.cookie.min"
    },
    shim:{
        'jquery.cookie':{
            'deps':['jquery']
        }
    }
};

We’ve defined a new top level configuration property named shim. This property is a javascript object of key value pairs. The key should be the name of your module (jquery.cookie above). The value is another javascript object that defines the shim configuration for this specific module.

There’s a number of different shim configuration options — the deps configuration option we’ve used above creates a source dependency (distinct from a normal define or requirejs module dependency) that ensures RequireJS will load each listed module (a single [jquery] above) entirely before the module that we’re “shimming” (jquery.cookie above).

The dep configuration option only scratches the surface of what shim is capable of — the shim documentation is worth reading if you’re interested in more details.

With the above configuration in place (and a jquery.cookie.min file in the Package_Module module), you should be able to safely create RequireJS programs that have the jquery cookie plugin added as a dependency.

Require vs. RequireJS

One last note before we wrap up. Throughout the RequireJS documentation, you’ll see reference to two functions

require()
requirejs();

What’s the difference between these two? There isn’t any, they’re the same function.

The AMD standard calls for a function that’s named require. However, RequireJS realized that there may already be code in use that defines a require function. In order to ensure their library could be used along side this code, they provide a requirejs alias to their main, AMD standard function.

Wrap Up

We’ve only scratched the surface of Magento’s use of javascript and modern frontend libraries in this article. However, all these systems are dependent on the RequireJS implementation. If you start there, you should be able to track back any javascript based feature to its inclusion via RequireJS, and through that figure out what’s going on. As always, knowing what a specific library does is always useful — but knowing how the framework your code lives in works is the key to becoming a more productive and rational programmer.

Originally published February 2, 2016

Magento 2: Adding Javascript and CSS via Layout XML

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

As promised, today we’re going to show you how to add javascript and CSS files to your Magento module without needing to worry about how their paths are rendered or how Magento serves them. While you may find this article useful on its own, we’re going to assume you’ve worked through the previous two.

Before we get to the main course, we’re going to provide an engineering critique of Magento 2’s XML based layout rendering language. This language is similar to the XML based language in Magento 1, but has some differences that might trip up an experienced Magento 1 developer.

The next few sections are optional, and not recommended unless you’re interested in the implementation details of Magento 2. You can skip ahead to the Adding CSS and Javascript to a Page section if all you’re interested in is getting your javascript and CSS files included on a page.

Magento 2’s Domain Specific Language for Rendering HTML

With Magento 2, the core team has added a number of features to their XML based layout language. They’ve also changed some of the language’s semantics. For example, in Magento 1, each node under the root node of a layout update XML file was always a layout handle. For example, in the Magento 1 Layout Update XML Node below, the handle is catalog_category_view.

<catalog_category_view>
    <block ...>
        <!-- ... -->
    </block>
</catalog_category_view>

Without getting too deeply into it, handles control which layout nodes are applied to a page during which request. Magento 2 still has handles, but they no longer appear in layout files. Instead, the handle is the file name. For example, the main catalog_category_view layout handle XML file is at

./vendor/magento/module-catalog/view/frontend/layout/catalog_category_view.xml

Other modules that listen for the catalog_category_view handle include

./vendor/magento/module-checkout/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-directory/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-google-optimizer/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-msrp/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-paypal/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-swatches/view/frontend/layout/catalog_category_view.xml
./vendor/magento/module-wishlist/view/frontend/layout/catalog_category_view.xml

In all the examples, the file name is the handle name. While the mechanism has changed, handles still serve the same purpose in Magento 2. On every Magento 2 HTML page render, certain handles “fire”, similar to events. The handles that fire control which layout files are loaded (in Magento 1 they controlled which XML nodes were read from all the files), and then Magento processes the combined XML tree to know which blocks it should add to a page.

Put another way, in Magento 1 you would

  1. Configure your module to load a layout update XML file (namespace_module.xml, catalog.xml etc.)
  2. In the layout update XML file you’d add a node for your handle (catalog_category_view)
  3. Under the catalog_category_view node you’d add your blocks

In Magento 2, you

  1. Add a layout handle XML file (catalog_category_view.xml)
  2. Under the root node of your layout handle XML file, add your blocks

We covered this change briefly in our Introduction to Magento 2 — No More MVC article. However, we glossed over a number of substantial changes to Magento’s layout language that we’ll need to touch briefly on before we continue.

New Nodes

In our Introduction to Magento 2 — No More MVC article, we created the following node in the layout handle XML file.

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<referenceBlock name="content">
    <block
        template="content.phtml"
        class="Pulsestorm\HelloWorldMVVM\Block\Main"
        name="pulsestorm_helloworld_mvvm"/>
</referenceBlock>

As a reminder, this XML is roughly equivalent to the following pseudo code.

//pseudo code -- does not work
$our_view_block = 
    $layout->createNewBlockWithClass('Pulsestorm\HelloWorldMVVM\Block\Main')
$our_view_block->setName('pulsestorm_helloworld_mvvm');
$out_view_block->setTemplate('content.phtml');
$layout->addBlockToContentContainer($our_view_block);

The <block/> tag from Magento 1 remains relatively unchanged. It means Create a block object. The main difference is the type attribute has been replaced with a class attribute. Since Magento did away with class aliases (core/template, namespace_module/my_block, etc.) it made sense to do away with the type attribute, and more accurately label it as class.

The first small change above is the referenceBlock node. Magento 1 had the concept of a block reference. However, the node was named <reference/>. In Magento 1, the above might look like

<reference name="content">
    <block
        template="content.phtml"
        class="Pulsestorm\HelloWorldMVVM\Block\Main"
        name="pulsestorm_helloworld_mvvm"/>
</block>

The referenceBlock node makes things more explicit, and is another welcome change. This might seem superficial, until you realize that Magento 2’s layout language controls more than blocks. The layout language also controls something called containers, and has a corresponding referenceContainer block. You can see an example of the referenceContainer block here

<!-- vendor/magento/module-checkout/view/frontend/layout/checkout_shipping_price_renderer.xml -->
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <referenceContainer name="root">
        <block class="Magento\Checkout\Block\Shipping\Price" name="checkout.shipping.price" as="shipping.price" template="shipping/price.phtml"/>
    </referenceContainer>
</layout>

In Magento 2, a container is a special sort of block that only contains other blocks. Containers are conceptually similar to the text/list blocks in Magento 1, although their implementation is very different.

The concept of containers is a good one, but it’s here that the implementation starts to get a little wobbly. Magento’s layout language is a little loosey–goosey with the difference between a container and a block. For example, the above XML?

<referenceContainer name="root">

This could also be written as

<referenceBlock name="root">

That is — even though root is a container, referenceBlock will still return a reference to it, and allow you to add blocks to it. For a change meant to make things more explicit and clear, it’s a little strange that the layout language would let something like that happen.

White Lies

Remember this XML from the introduction article?

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<referenceBlock name="content">
    <block
        template="content.phtml"
        class="Pulsestorm\HelloWorldMVVM\Block\Main"
        name="pulsestorm_helloworld_mvvm"/>
</referenceBlock>

Well, it turns out that the content block is actually a container. The above should have been written as

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<referenceContainer name="content">
    <block
        template="content.phtml"
        class="Pulsestorm\HelloWorldMVVM\Block\Main"
        name="pulsestorm_helloworld_mvvm"/>
</referenceContainer>

We used referenceBlock in our introduction tutorial because we weren’t ready to discuss containers and other changes to the layout system. While this was useful for a transitional tutorial, generally speaking this is the sort of looseness that can make a domain specific language seem extra confusing.

Without getting too deeply into the details, you can tell if a “block” is a container or a regular block by how the original programmer created it. If you see a <block/> tag

<block name="foo" />

then the named block (“foo” above) is a regular block. If you see a <container/> tag

<container name="foo"/>

then the named entity is a container. If you’re curious, Magento’s core code adds the content container in the following file

<!-- vendor/magento/module-theme/view/frontend/layout/default.xml -->
<container name="content" label="Main Content Area"/>

Also, notice the default.xml file name? That’s equivalent to Magento 1’s <default/> handle node.

Context Sensitive Nodes

In Magento 1, the layout language was a system designed to render arbitrary HTML via a nested collection of block objects. The layout system itself didn’t care which part of an HTML document it was rendering. It just rendered blocks. Specific blocks, like page/html_head, could introduce that context, but it happened at the block level. The layout system itself was unaware that it was rendering the <head/> portion of a document.

In Magento 2, the core team attempted to change this, and add that context in at the language level. They added two new top level tags named <body> and <head> to the vocabulary of the language. While it was an interesting experiment, the implementation feels half done, and further complicates an already complicated layout system. Consider the following

<!-- File: vendor/magento/module-backend/view/adminhtml/layout/default.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <title>Magento Admin</title>
        <meta name="viewport" content="width=1024, initial-scale=1"/>
        <link src="requirejs/require.js"/>
        <css src="extjs/resources/css/ext-all.css"/>
        <css src="extjs/resources/css/ytheme-magento.css"/>
    </head>
    <body>
        <attribute name="id" value="html-body"/>
        <block name="require.js" class="Magento\Backend\Block\Page\RequireJs" template="Magento_Backend::page/js/require_js.phtml"/>
        <referenceContainer name="global.notices">
            <block class="Magento\Backend\Block\Page\Notices" name="global_notices" as="global_notices" template="page/notices.phtml"/>
        </referenceContainer>
        <!-- ... -->
    </body>
</page>

Here you can see an example of a core module layout handle XML file that uses the new head and body sections. The first bit of confusion this introduces is top level tags under the root tag now mean different things. In some files, these top level tags will be context tags like <head/> and <body/> above. In other files, the top level tags will be actual commands/directives (referenceBlock, container, etc) for the layout engine

<!-- File: vendor/magento/module-bundle/view/base/layout/catalog_product_prices.xml -->
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <referenceBlock name="render.product.prices">
    <!-- ... -->
</layout>

If you enjoy implementing domain specific languages, this may seem like a minor thing. However, the intent of a domain specific language is to simplify and constrain the options for developers and programmers unfamiliar with the entire system. The lack of consistency here will make these files harder for designers and front end developers to understand.

The next bit of confusion is in what that context change means. Nodes placed inside the <body/> tag

<!-- File: vendor/magento/module-backend/view/adminhtml/layout/default.xml -->    
<body>
    <attribute name="id" value="html-body"/>
    <block name="require.js" class="Magento\Backend\Block\Page\RequireJs" template="Magento_Backend::page/js/require_js.phtml"/>
    <referenceContainer name="global.notices">
        <block class="Magento\Backend\Block\Page\Notices" name="global_notices" as="global_notices" template="page/notices.phtml"/>
     </referenceContainer>
     <!-- ... -->
</body>

behave very similar to plain old layout XML nodes. You’re still getting references to existing blocks and containers, and adding new blocks to them for rendering. The only difference is the <attribute/> tag you see above. With this you can change the ID element of the underlying <body/> tag.

When you shift into <head/> context, you’re in a difference world.

<!-- File: vendor/magento/module-backend/view/adminhtml/layout/default.xml -->    
<head>
    <title>Magento Admin</title>
    <meta name="viewport" content="width=1024, initial-scale=1"/>
    <link src="requirejs/require.js"/>
    <css src="extjs/resources/css/ext-all.css"/>
    <css src="extjs/resources/css/ytheme-magento.css"/>
</head>

Here, you’ve completely lost the ability to modify the layout with commands like referenceBlock, etc. Instead, you have a narrow set of tags (<attribute/>, <css/>, <link/>, <meta/>, <remove/>, <script/>, <title/>) for doing things specifically in the <head/> of a document.

The other bit of cognitive dissonance a Magento 1 developer will feel here is the <head/> section of the HTML page is no longer rendered like a normal block. If you take a look at the root phtml template you can see a Magento HTML page is no longer a series of nested blocks.

<!-- File: vendor/magento/module-theme/view/base/templates/root.phtml -->
<!doctype html>
<html <?php /* @escapeNotVerified */ echo $htmlAttributes ?>>
    <head <?php /* @escapeNotVerified */ echo $headAttributes ?>>
        <?php /* @escapeNotVerified */ echo $requireJs ?>            <?php /* @escapeNotVerified */ echo $headContent ?>            <?php /* @escapeNotVerified */ echo $headAdditional ?>        </head>
    <body data-container="body" data-mage-init='{"loaderAjax": {}, "loader": { "icon": "<?php /* @escapeNotVerified */ echo $loaderIcon; ?>"}}' <?php /* @escapeNotVerified */ echo $bodyAttributes ?>>
        <?php /* @escapeNotVerified */ echo $layoutContent ?>        </body>
</html>

In Magento 2, an HTML page is a phtml template populated by simple variables. These simple variables are populated by different means in the render method of the Magento\Framework\View\Result\Page object. Magento creates the <body/> tag of the page by echoing out the $layoutContent variable. Magento gets the string for $layoutContent by doing the traditional kickoff of rendering a series of nested blocks.

#File: vendor//magento/framework/View/Result/Page.php
$output = $this->getLayout()->getOutput();
$this->assign('layoutContent', $output);
//...
<?php /* @escapeNotVerified */ echo $layoutContent ?>

Magento renders the <head/> section of an HTML page by echoing several variables.

<head <?php /* @escapeNotVerified */ echo $headAttributes ?>>
    <?php /* @escapeNotVerified */ echo $requireJs ?>        <?php /* @escapeNotVerified */ echo $headContent ?>        <?php /* @escapeNotVerified */ echo $headAdditional ?>    </head>

How Magento populates of contents of these variable is beyond the scope of this article. The main change you’ll want to be aware of is that <head/> is no longer simply controlled by standard layout blocks.

Summary of Magento 2 Layout Changes

Magento 1’s layout system, while cryptic, was ultimately understandable by a single developer. Its why I wrote No Frills Magento Layout — the system wasn’t well documented, but once explained developers could understand and reason about it from top to bottom. It had a complex looking surface, but a simple elegant implementation.

As you can see from the above critique, Magento 2 has taken an already cryptic system, and added layers of complexity on top of it. These top levels of complexity are equally complex under the hood. Without getting into the details of it, Magento takes the layout handle XML files for a single request, merges them into a a single document, processes that document to transform it into a Magento 1 style page layout document, and then processes that document is way that similar, but not identical, to Magento 1.

Unlike Magento 1’s layout system, which an average developer could ultimately translate in their head into PHP code and reason about, the new rendering is too complex for most human beings to keep in their head at once. The new system is less understandable to the average developer. Perhaps this was necessary to implement the RequireJS and Less CSS systems the core team wanted to, but from the outside looking in it seems like a classic case of what people complain about when they complain about architect driven development.

Adding CSS and Javascript to a Page

Layout system critiqued, let’s get back to the practical business of adding a front end file to our Magento module. Before we begin, per pervious articles in this series, the following assumes

  1. You’re working with developer mode enabled (SetEnv MAGE_MODE developer in your apache config)
  2. That you’ve disabled the full page caching in System -> Cache Managment

To start, we need to create a new Magento module. We’re going to use the Magento code generating tool pestle for this, but if you want to create your own module manually you can follow the instructions in our Introduction to Magento 2 — No More MVC article.

To create the new module using pestle, run the following commands

$ pestle.phar generate_module Pulsestorm JavascriptCssExample 0.0.1
$ pestle.phar generate_route Pulsestorm_JavascriptCssExample frontend pulsestorm_javascriptcssexample
$ pestle.phar generate_view Pulsestorm_JavascriptCssExample frontend pulsestorm_javascriptcssexample_index_index Main content.phtml

Then enable your new module

$ php bin/magento module:enable Pulsestorm_JavascriptCssExample

and let the module setup system know about your module.

$ php bin/magento setup:upgrade

After running the above, you should be able to load the pulsestorm_javascriptcssexample frontname in your system.

http://magento.example.com/pulsestorm_javascriptcssexample

With the above completed, let’s get started!

The Layout Head Section

One of the new features Magento 2 introduces is context aware layout update XML files. By context aware we mean that end-programmer-users can add commands/directives to their layout XML files that only effect a particular section of the document. In plain english — layout update XML files now have a <head/> section where you can add head specific information about a file.

Sample code is often worth 1,000 words. Open up your module’s layout handle XML file

<!-- File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/layout/pulsestorm_javascriptcssexample_index_index.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <referenceBlock name="content">
        <block template="content.phtml" class="Pulsestorm\JavascriptCssExample\Block\Main" name="pulsestorm_javascriptcssexample_block_main" />
    </referenceBlock>
</page>

and add the following <head/> node

<!-- File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/layout/ -->pulsestorm_javascriptcssexample_index_index.xml 
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Pulsestorm_JavascriptCssExample::test.css"/>
        <link src="Pulsestorm_JavascriptCssExample::test.js"/>
    </head>
    <referenceBlock name="content">
        <block template="content.phtml" class="Pulsestorm\JavascriptCssExample\Block\Main" name="pulsestorm_javascriptcssexample_block_main" />
    </referenceBlock>
</page>

After making the above changes, clear your cache,

$ php bin/magento cache:clean

and then reload the Magento page

http://magento.example.com/pulsestorm_javascriptcssexample

If you view the source to this page, you should see the following HTML

<link  rel="stylesheet" type="text/css"  media="all" href="http://magento-2-with-keys.dev/static/frontend/Magento/blank/en_US/Pulsestorm_JavascriptCssExample/test.css" />
<!-- ... -->
<script  type="text/javascript"  src="http://magento-2-with-keys.dev/static/frontend/Magento/blank/en_US/Pulsestorm_JavascriptCssExample/test.js"></script>

That is, Magento has automatically created the http (or https) paths we’ve been generating manually so far.

http://magento-2-with-keys.dev/static/frontend/Magento/blank/en_US/Pulsestorm_JavascriptCssExample/test.css
http://magento-2-with-keys.dev/static/frontend/Magento/blank/en_US/Pulsestorm_JavascriptCssExample/test.js

When you use a string identifier like this

Pulsestorm_JavascriptCssExample::test.css

you’re telling Magento

use the test.css file found in the Pulsestorm_JavascriptCssExample module.

Without these Vendor_Module::... identifiers Magento would try loading these files from the theme hierarchy.

With the URLs generated, if you add corresponding files to your module at

#File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/web/test.js
alert("hello");

#File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/web/test.css
body{
    background-color:#f00;
}

and reload the page, you’ll see that Magento has loaded them correctly into the system.

Adding Files Via PHP

In addition to using Magento 2’s layout XML system to automatically add front end asset URLs to your project, you can also create these URLs via PHP using a Magento\Framework\View\Asset\Repository object. We’ll show you how to do this below, as well as how to add arbitrary HTML to the <head/> of a Magento HTML page.

Starting with the later item, add the following node to our layout handle XML file

<!-- File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/layout/ pulsestorm_javascriptcssexample_index_index.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <!-- ... -->
    <referenceBlock name="head.additional">
        <block  template="head.phtml" 
                class="Pulsestorm\JavascriptCssExample\Block\Head" 
                name="pulsestorm_javascriptcssexample_block_head" />
    </referenceBlock>
    <!-- ... -->
</page>

The above code

  1. Gets a reference to the head.additional block
  2. Creates a new Pulsestorm\JavascriptCssExample\Block\Head block named pulsestorm_javascriptcssexample_block_head that uses the head.phtml template.
  3. Adds that new block to the head.additional block using the reference from #1

The head.additional block is a special block. Any block added to head.additional will automatically be output into the <head/> area of a Magento page. If you read our critique above, this is another bit of confusion added by the <head/> context. Even though our ultimate goal is to add something to <head/>, we need to operate inside the layout handle XML file’s <body/> tag.

Regardless, once we’ve got the layout XML in place, we’ll want to create our new Head block class

#File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/templates/head.phtml
<?php
namespace Pulsestorm\JavascriptCssExample\Block;
class Head extends \Magento\Framework\View\Element\Template
{
}

As well as a template

#File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/templates/head.phtml
<!-- Hello There -->

With the above in place, clear your Magento cache and reload your page. You should see the <!-- Hello There --> comment in your page’s <head/> node.

With a new template rendered in <head/>, we’re ready to render an asset URL using the asset repository.

The Asset Repository

The Magento\Framework\View\Asset\Repository object will allow us to create asset objects. Asset objects can convert a file identifier like foo/test.js or Pulsestorm_JavascriptCssExample::test.js into a full URL.

Like any object in Magento 2, when we want an instance of an object we don’t directly instantiate it — we inject it in another object’s constructor. Change your Head.php file so it matches the following

#File: app/code/Pulsestorm/JavascriptCssExample/view/frontend/templates/head.phtml
<?php
namespace Pulsestorm\JavascriptCssExample\Block;
class Head extends \Magento\Framework\View\Element\Template
{
    public $assetRepository;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context, 
        array $data = [],    
        \Magento\Framework\View\Asset\Repository $assetRepository
    )
    {
        $this->assetRepository = $assetRepository;
        return parent::__construct($context, $data);
    }
}

What we’ve done above is use Magento 2’s automatic constructor dependency injection to create a \Magento\Framework\View\Asset\Repository object, and assign it to the assetRepository property of our block object. The other parameters in __construct and the call to parent::__construct are there for compatibility with the base template block class. Also, notice we made assetRepository a public property. This means we’ll be able to access it in our phtml template.

Edit your head.phtml file so it matches the following.

#File: app/code/Pulsestorm/JavascriptCssExample/Block/Head.php
@highlightsyntax@php
<?php
    $asset_repository = $this->assetRepository;
    $asset  = $asset_repository->createAsset('Pulsestorm_JavascriptCssExample::test.js');
    $url    = $asset->getUrl();
?>
<!-- Hello There -->
<script src="<?php echo $url; ?>"></script>

With the above in place, clear your cache, delete the files in var/generate/* (because you changed an automatic constructor dependency injection constructor), and reload the page. If you view the raw HTML source, you should see a new <script/> tag rendered with a full asset URL.

What we’ve done above is use the createAsset method of the asset repository object to create an asset object. Then, we use the getUrl method of the asset object to fetch the HTTP url of the asset. All we need to know is the file identifier — Magento handles the grunt work of pulling together the correct URL path parameters.

Wrap Up

Today, after a long winded critique of Magento 2’s layout language, we demonstrated how to use that language to add front end CSS and Javascript assets to a Magento page. We also investigated directly using the underlying PHP asset repository that makes this possible.

In the past few articles, we’ve been entirely focused on getting “raw” front end asset files into our system. Next time we’ll start investigating how Magento has integrated with the new higher level front end abstractions like RequireJS and Less CSS.

Originally published January 13, 2016