Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

How to Create a Magento 2 Theme from the Scratch

5.00/5 (2 votes)
4 Oct 2016CPOL10 min read 18.7K  
Learn how to create a new theme for your ecommerce website using Magento 2.

In this article, I’d like to share a little bit of my knowledge and expertise about creating a brand-new theme using Magento 2. This CMS has lots of advantages in comparison to the first version of Magento. Most of them have to do with the client side. Here are some of these advantages:

  • Full-scale support of HTML5 and CSS3;
  • Built-in LESS preprocessor;
  • Asynchronous module upload with RequireJS (without having to add code to head manually);
  • Use of jQuery/jQuery UI instead of Prototype library;
  • Use of MagentoUI library (it includes all the necessary components for simple and flexible rendering of user interface).

As to the development of new themes, Magento 2 features a new module structure for its scripts and code. Now, all static css, js and image files are stored in the special web theme’s folder. Skin folder was deleted from Magento’s root directory. View folder was introduced to store layouts and files that provide MVC access for this or that specific module.

So let’s try to create a brand-new theme with Magento 2, having a more detailed look at the structure of directories and files.

Creation of Theme’s Directory

Magento 2 themes are stored in <M2 root directory>/app/design/frontend catalogue. To begin with, you should create Vendor folder (it was called like that in Magento 1) and then create a new folder within it for your theme.

For example: <M2 root directory>/app/design/frontend/Singree/walkbeyond. Where Singree is vendor and walkbeyond is theme’s code. You can use any combinations of letters and figures in the code part.

After the directories above are created, you need to declare the theme to activate it on the back-end side.

Declaration and Registration

To let Magento 2 ‘see’ a newly created theme, you should create the following file: <Magento 2 root directory>/app/design/frontend/<vendor>/<theme codename>/theme.xml with the following code within:

<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/theme.xsd">
<title>walkbeyond</title>
<parent>Magento/blank</parent>
<media>
<preview_image>media/walkbeyond.jpg</preview_image>
</media>
</theme>

Include the theme’s name in <title> tag; a parent theme – in <parent>. (All non-found, static files and layout files will be taken from the parent theme.) In the example, you can see blank that comes in with Magento 2 and acts as a basic theme. It doesn’t have a parent theme. However, the number of inheritance levels is not limited in Magento 2; there are no limits, which makes it more useful than Magento 1.

In <preview_image> tag you can choose and set a preview for your theme. It will be displayed in the admin part. To register the theme in the system, you need to create registration.php file in the root. Like that:

PHP
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::THEME,
    'frontend/Singree/walkbeyond',
    __DIR__
);

It’s not that necessary but you can also create composer.json file to share the theme as composer package. Here’s an example:

PHP
{
    "name": "Singree/walkbeyond",
    "description": "N/A",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "Singree/walkbeyond": "100.0.*",
        "magento/framework": "100.0.*"
    },
    "type": "magento2-theme",
    "version": "100.0.1",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [
            "registration.php"
        ]
    }
}

Directory Creation for Static Files

To store styles, JavaScript code, images and fonts, you should create web folder in the theme’s root directory. It needs to have the following structure:

app/design/frontend/Singree/walkbeyond/
├── web/
│ ├── css/
│ │ ├── source/
│ ├── fonts/
│ ├── images/
│ ├── js/

All these folders are not that crucial to have, though. In images folder all the static files are stored. You can also add logo.svg (default name) to re-declare the theme’s logo into that folder.

Based on the features of blank parent theme, you can create _theme.less file with re-declaration of basic variables of MagentoUI in css/source folder. Then, in <magento-root>/lib/web/css/source/lib/variables/ folder do some search within the original files. You need to find default values for the variables you want to re-declare. In css/source folder you can set up and declare styles for your modules in _module.less file and widgets in _widgets.less file. Create _extend.less file for minor customization.

Image Configuration

Your theme is obligatory to include etc/view.xml file (if it’s not declared in the parent theme) that stores all the necessary values for product images, such as width, height, background color, opacity, etc. You can set up values using ID attributes and types within the element. Here’s how it is done:

XML
<images module="Magento_Catalog">
...
<images/>

For example, utilizing the described above process you can easily change image size in table view of your catalogue (as shown below).

XML
<?xml version="1.0"?>
<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/view.xsd">
    <media>
        <images module="Magento_Catalog">
            <image id="category_page_grid" type="small_image">
                <width>200</width>
                <height>200</height>
            </image>
</media>
</view>

Category_page_grid ID is unique within the theme. Small_image type corresponds to Small Image Role on the admin’s side. Here are some values you can use for image types: image, small_image, swatch_image, swatch_thumb and thumbnail.
Image 1
When the product is saved, all images are cached up. After you change the image size, you can execute a command php <magento install dir>/bin/magento catalog:images:resize to generate the images. As a result, we are going to get the following structure:
Image 2

Theme Activation on the Admin Side

After the theme has been created in the file system, you can activate it in the settings. To do that, just follow the route Content > Design > Themes and check if the theme is present in the list.

Then, access CONTENT > Design > Configuration and press edit for a selected website or ecommerce platform. Select our theme (Applied Theme) and press Save configuration.

In case your cache is on, flush it when the theme is applied. To do that, go to System > Cache Management and update all the invalid cache types.

As a result, we are going to get the tried-and-true magento/blank theme featuring the different logo and resized images.
Image 3

Use of LESS for Style Editing

After you’ve created the theme itself, you can tinker a bit with the styles to change a website’s pages visually. To edit the project’s styles, you can use one of the two types of LESS preprocessing in Magento 2:

  • Server-side compilation of LESS;
  • Client-side compilation of LESS using less.js.

Client-side compilation is usually used in the development mode as all changes and edits can be visualized right away: a browser starts to compile files every time when you refer to style files. Meanwhile, when the server-side compilation is in place, you need to manually delete the content of pub/static and var/view_preprocessed folders. You can optimize the process with Grunt task runner, though. It can track every action within the files, ‘clean’ the selected folders and compile less automatically.

To change the type of compilation, access the admin board: Stores > Configuration > Advanced > Developer > Front-end development workflow > Workflow.

Image 4

Our example is rather simple, so we will use server-side compilation (set by default).

Let’s apply background color and fonts for our website in _theme.less file:

XML
@page__background-color: #484848;
@text__color: #fff;
@font-family__base: 'Arial', sans-serif;

After you’ve deleted the contents in /pub/static/frontend/Singree/walkbeyond/en_US and var/view_preprocessed directories, you can see how the visual part of your website has changed.

Image 5

Use of Magento 2 for Theme Layouts

Magento 2 enables you to extend and override layouts for this or that theme. You don’t need to copy page-related or general layouts from the parent theme. You only have to refer to a selected layout in the theme’s folder as shown below:

<theme_dir>
 |__/<Namespace>_<Module>
   |__/layout
     |--<layout1>.xml
     |--<layout2>.xml

For instance, to extend a layout catalog_category_view for Catalog module that is stored in <сatalog_module_dir>/view/frontend/layout/catalog_category_view.xml, you should create the following route file:

<theme_dir>/Magento_Catalog/layout/catalog_category_view.xml.

To delete any block (e.g. category description) from your layout, you can:

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">
    <body>
        <referenceBlock name="category.description" remove="true"/>
    </body>
</page>

However, layout extensions can’t be used for any customization task. If you need to customize lots of things, it’s better to override the layout. That is, it’s easier to create and use a new file rather than use a file from the parent theme (your main layout). Here are some customization examples when you better override the layout:

  1. To request a method that ‘dominates’ another method of the parent layout;
  2. To change the number of arguments to the method;
  3. To cancel the delete command for block/container using remove attribute;
  4. To install XML attributes for blocks/containers;
  5. To delete arguments to the blocks;
  6. To delete layout handles by overriding the layout file with ‘blank’ layout handle;
  7. To modify the activation of layout handles.

You can override the basic layouts by creating override/base folder in the module’s theme folder as shown below:

<theme_dir>
  |__/<Namespace_Module>
    |__/layout
      |__/override
         |__/base
           |--<layout1>.xml
           |--<layout2>.xml

These files are able to override the layout:

  • <module_dir>/view/frontend/layout/<layout1>.xml
  • <module_dir>/view/frontend/layout/<layout2>.xml

Also, you can override the layouts for the parent theme by creating override/theme folder in the module’s theme folder:

<theme_dir>
  |__/<Namespace_Module>
    |__/layout
      |__/override
         |__/theme
            |__/<Parent_Vendor>
               |__/<parent_theme>
                  |--<layout1>.xml
                  |--<layout2>.xml

These files are able to override the layout that are stored by the following routes:

  • <parent_theme_dir>/<Namespace>_<Module>/layout/<layout1>.xml
  • <parent_theme_dir>/<Namespace>_<Module>/layout/<layout1>.xml

Theme Templates

Magento 2 themes enable you to override not only layout modules but also templates. To override a module’s template <module_dir>/view/frontend/templates/<path_to_templates>, create a new folder templates in the theme’s module folder: <theme_dir>/<Namespace>_<Module>/templates/<path_to_templates>

Let’s try to add a text message for a cart file Singree/walkbeyond/Magento_Checkout/templates/cart/minicart.html. The message should be about the number of products in the cart. Here’s how:

PHP
<div data-block="minicart" class="minicart-wrapper">
    <a class="action showcart" href="<?php /* @escapeNotVerified */ echo $block->getShoppingCartUrl(); ?>"
       data-bind="scope: 'minicart_content'">
        <span class="cart-title hidden-xs"><?php /* @escapeNotVerified */ echo __('Shopping cart'); ?></span>
        <span class="counter total-qty empty"
              data-bind="css: { empty: cart().summary_count == 0 }, blockLoader: isLoading">
        <?php /* @escapeNotVerified */ echo __('Products count:'); ?>    <span class="counter-number"><!-- ko text: cart().summary_count --><!-- /ko --></span>
            <span class="counter-label">
            <!-- ko if: cart().summary_count -->
                <!-- ko text: cart().summary_count --><!-- /ko -->
                <!-- ko i18n: 'items' --><!-- /ko -->
            <!-- /ko -->
            </span>
        </span>
    </a>
    <?php if ($block->getIsNeedToDisplaySideBar()): ?>
        <div class="block block-minicart empty"
             data-role="dropdownDialog"
             data-mage-init='{"dropdownDialog":{
                "appendTo":"[data-block=minicart]",                "triggerTarget":".showcart",
                "timeout": "2000",                "closeOnMouseLeave": false,
                "closeOnEscape": true,                "triggerClass":"active",
                "parentClass":"active",        "buttons":[]}}'>
            <div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
                <!-- ko template: getTemplate() --><!-- /ko -->
            </div>
        </div>
    <?php endif ?>
    <script>
        window.checkout = <?php /* @escapeNotVerified */ echo \Zend_Json::encode($block->getConfig()); ?>;
    </script>
    <script type="text/x-magento-init">
    {
        "[data-block='minicart']": {
            "Magento_Ui/js/core/app": <?php /* @escapeNotVerified */ echo $block->getJsLayout();?>
        },
        "*": {
            "Magento_Ui/js/block-loader": "<?php /* @escapeNotVerified */ echo $block->getViewFileUrl('images/loader-1.gif'); ?>"
        }
    }
    </script>
</div>

Theme Localization

Theme localization is done utilizing an array of vocabularies. You can search for vocabularies here:

  • <parent_theme_dir>/i18n/ (to view all the parent themes)
  • <current_theme_dir>/i18n/

I18n folder can be stored in every module or globally in the app’s folder. Vocabularies with theme’s folders are high priority in search of the translation bar.

To generate a file with translation in the theme’s folder, you can use i18n tool. You can also fire up the following command in the root catalogue of Magento 2

php bin/magento i18n:collect-phrases --output="app/design/frontend/Singree/walkbeyond/i18n/en_US.csv" app/design/frontend/Singree/walkbeyond

These command with collect all strings into a vocabulary. Here’s the file:

app/design/frontend/OrangeCo/orange/i18n/en_US.csv

You can open it in any table editor (such as Excel) and change the translations values for every phrase in the right column. You will be able to see the translated strings after the theme has been applied.

Theme Deletion

If your theme is Composer package, you can delete it from the root catalogue with the following command:

php bin/magento theme:uninstall [-c|--clear-static-content] {theme path} ... {theme path}

{theme path} is your approximate route to the theme. In our case it is frontend/Singree/walkbeyond.

--clear-static-content deletes static files that don’t need automatic generation: css, js, images.
If your theme is not a Composer package, you will have to do the following to delete it:

  • Delete the theme’s folder app/design/frontend/<Vendor>/;
  • Delete the contents of var/view_preprocessed/;
  • Delete the contents of pub/static/frontend/;
  • Open Magento 2 database and find theme table to delete a string with the theme’s name;
  • To flush and delete cache using php bin/magento cache:flush command.

Bottom Line

In this article I’ve talked about the main components of theme creation using Magento 2. I’ve described how to create the structure for directories and files (themes, images, previews). Also, I’ve touched the process of how to override styles, templates and layouts of a given theme. I’ve shown how to translate a website using online vocabularies.

Here’s the list of folders you need to have to create a theme:

  1. /<Vendor>_<Module> (not necessary; stores styles, templates and layouts of a given module)
  2. /<Vendor>_<Module>/web/css/source (not necessary; stores specific a given module: .ccs and .less files)
  3. /<Vendor>_<Module>/layout (not necessary; stores main extended packages or parent theme layouts)
  4. /<Vendor>_<Module>/layout/override/base (not necessary; stores overriding layouts for the main theme)
  5. /<Vendor>_<Module>/layout/override/<parent_theme> (not necessary; stores overriding layouts for the parent theme)
  6. /<Vendor>_<Module>/templates (not necessary; stores templates that override main module templates or parent templates)
  7. /etc/view.xml (necessary if the theme is not declared; stores image configuration data)
  8. /i18n (not necessary; stores .csv vocabularies for translations)
  9. /media (not necessary; stores previews)
  10. /web (not necessary; static files that can be uploaded from front-end)
  11. /web/css/source (not necessary; stores global elements of Magento UI library and _theme.less file that overrides the values for variables by default)
  12. /web/fonts (not necessary; stores theme’s fonts)
  13. /web/images (not necessary; stores theme’s images)
  14. /web/js (not necessary; stores javascript files)
  15. /composer.json (not necessary; describes theme’s variables and metadata)
  16. /registration.php (necessary; used for theme registration)
  17. /theme.xml (not necessary; stores theme’s basic data and metadata; use it in case the theme is declared as a system component)

I’ve described the larger part of the structure above in the article. Also, It’d make sense to mention some rules of working with javascript client libraries (like require.js) or describe how to operate with blocks and containers, but this way the article would get too enormous. I hope the information I’ve provided above is enough.

Thanks for your time!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)