How to Build a Theme in Magento 2
How to Build a Theme in Magento 2

How to Build a Theme in Magento 2

Published March 6, 2018 in Development
Does Social Media Affect My SEO?
February 22, 2018
How to Create a Payment Method in Magento 2
March 27, 2018

When building a custom theme for Magento, it’s important to follow best practices for how the system is designed to be extended. Magento 2’s frontend is significantly different than that of Magento 1, so even for seasoned Magento developers, there are plenty of things worth noting.
This article is not an exhaustive step-by-step guide to frontend customization. It’s primarily meant to outline the basics for how the system is structured and how to best extend it, as well as to serve as a reference for various theme topics. In general, Magento’s official DevDocs are a good resource for specific tasks.

I’ll start by recommending a global-first approach to theme building. By implementing key branding elements such as logo, typography, and theme colors, we’ll stick to the critical path and create a foundation upon which the rest of the theme can be built. For example, when we’re styling our product listing page, we shouldn’t have to worry about what the add-to-cart buttons looks like, because they should probably match the rest of the buttons on the site, which should all be styled at a global scope.
We’ll outline several areas of theme building below (such as logo, typography, icons, and global styles) that can be used to quickly get a site looking distinctly branded.

Creating a new theme

For a new theme, we’ll need to create registration.php and theme.xml in our theme directory, and then run $ bin/magento setup:upgrade.

registration.php registers the theme as a system component at the specified location (as with modules).

Example:

theme.xml defines basic theme configuration, including at least the title and usually the parent theme that’s being extended.

Example:

See more in DevDocs under ‘Create a new storefront theme’.

Note: It’s recommended that all custom themes extend Magento/blank instead of Magento/luma, since the Luma theme is only intended to be an example.

The logo is an important part of the theme, since it’s the primary identity of the site that first greets the user. We strongly recommend using an SVG vector image whenever possible, since it will scale to any screen resolution or zoom level.

Magento 2 uses an SVG logo by default, and it will find yours automatically if you put it in your theme directory at: web/images/logo.svg.

You may also need to specify the dimensions in: Magento_Theme/layout/default.xml.

Example:

Product Images

Images for catalog products can be sized and re-sized in Magento without having to change any styles. Default configuration is found in the Magento Blank theme, in etc/view.xml. Configuration can be customized by redefining values in etc/view.xml in our theme directory.

Example:

Let’s set different widths and heights for images in the grid view and list view on the category page:

There’s also a section in etc/view.xml for configuring several aspects of the image gallery on the product detail page.

Example:

Let’s set the gallery thumbnails to display vertically instead of horizontally:

See more in DevDocs under ‘Configure images properties for a theme’.

UI Library

The Magento 2 theme was built using a system of LESS mixins (collectively the “UI Library”), which are located in: /lib/web/css/source/lib. Each of the LESS files has extensible mixins that are used to define the look of global elements in Magento Blank, using default variables defined in: /lib/web/css/source/lib/variables.

Example:

  • Default button styles come from the .lib-button() mixin in: /lib/web/css/source/lib/_buttons.less.
  • The variables used to set the default values for .lib-button() come from: /lib/web/css/source/lib/variables/_buttons.less.

These mixins can be called (or redefined if necessary) when building a custom theme, but many of the mixins are flexible enough that a lot of global styles can (and should) be customized simply by redefining the default variables. By redefining a value (like a button background color) at a low level, we can recompile styles one time and immediately see the change reflected across the entire site.

The UI library has some built-in documentation in: /lib/web/css/source/docs. It’s possible to access it on the frontend of your site with some changes to .htaccess, but the simplest way is to visit one of many publicly hosted demos, such as this one by Nexcess. It’s worth reading through the introduction page of this documentation, as well as at least skimming through each of the pages linked in the “files” dropdown at the top, to familiarize yourself with how the mixins are used and what the default variables are. This can also be helpful (when compared against the Magento Blank frontend) to better understand the approach that was used to build the theme, and to understand the layout of the files in: /lib/web/css/source/lib.

Styling (CSS and LESS)

Style preprocessing (LESS)

Magento 2 has a lot of LESS files scattered throughout the codebase by default, which can initially be pretty confusing. This assortment is due to the modularity of the system and the usage of /var/view_preprocessed for compilation. This allows styles to be separated in the system (by library, theme, and module) but merged in /var/view_preprocessed during compilation, to enable the use of relative import paths. From there, the CSS is compiled into /pub/static/frontend to be served to the user agent.

The flexible import structure can be seen by observing a specific example in the Magento Blank theme, in web/css/styles-m.less:

  • Line 31 shows this import directive: @import 'source/lib/_responsive.less'.
  • No file exists in the theme at web/css/source/lib/_responsive.less, the relative path this would seem to point to.
  • Where this file does exist is in /lib/web/css/source/lib/_responsive.less.

How this works in action can be seen in /var/view_preprocessed once static asset deployment is performed.

  • /var/view_preprocessed/css/frontend/Magento/blank/en_US/css/styles-m.less contains the same import directive.
  • The relative path of the import now correctly refers to /var/view_preprocessed/css/frontend/Magento/blank/en_US/css/source/lib/_responsive.less, which can be seen to be identical to the source file in /lib.

Keeping this construct in mind can make it easier to understand the LESS file structure between library and modules.

See more in DevDocs under ‘CSS preprocessing’.

Usage of LESS files

Besides the files in /lib (mentioned in the “M2 UI Library” section above), which you may find cause to reference often during a theme build, some other key files to know how to use are: _theme.less, _module.less, _extend.less, and _styles.less. Each of these will be included by the system automatically (without a custom @import directive), from various locations.

  • _theme.less is for redefining existing variables, as well as for defining new variables and/or mixins that apply to your theme globally.
  • _module.less is intended to define original styles for a specific extension. Files with this name will not usually be included in a theme.
  • _extend.less is used in multiple places within the theme to modify or extend existing styles for specific extensions.
  • _styles.less is primarily reserved for imports and can be pulled forward from the parent theme to add new custom LESS files.

I’ve spared most of the details for the usage of these files, as I recommend checking out Classy Llama’s article ‘A Look at CSS and Less in Magento 2‘ for more information.

Grunt

When Magento 2 is in production mode, if styles are changed, they must be recompiled by running $ bin/magento setup:static-content:deploy. This is a long process that goes through every static file for all themes. When the system is in default or developer mode, static files are automatically generated by the system when needed, which is also a long process, and once styles have been generated, changes are not picked up automatically. Styles can be recompiled manually by re-deploying static content, but the most efficient method is to use a task runner, especially when doing active theme development. Magento 2 includes built-in support for compiling LESS with Grunt. The process is outlined in DevDocs under ‘Compile LESS with Grunt’, but here are a few things that are helpful to remember:

  • Before running the npm installer, remove the ‘.sample’ suffix from Gruntfile.js.sample and package.json.sample in your site root.
  • There are a couple things to note under “Installing and configuring Grunt” in the linked doc above.
    • The <theme> value should be whatever you prefer to use on the command line when running Grunt.
    • The “name” value must match the registered theme name as it appears in the registration.php file for your theme, in the string beginning with “frontend/”. (For example, in the registration.php file for Magento Blank, the 2nd parameter passed to register() is “frontend/Magento/blank”, so the “name” value in /dev/tools/grunt/configs/themes.js is “Magento/blank”.)
  • Any time that LESS files are added or deleted, run grunt exec:<theme>, followed by grunt less:<theme>.
  • If changes were made only to existing LESS files, you should only have to run grunt less:<theme>.
  • You can use grunt watch and grunt watch:<theme> to detect changes to LESS files and automatically recompile styles.
    • If you have LiveReload installed, this can be a convenient way to avoid having to refresh your browser.

Fonts/Typography

Default font families are declared in the Magento Blank theme, in web/css/source/_typography.less. We can define our own custom fonts using the same structure by creating and including our own _typography.less file in our theme. The @font-path passed to the .lib-font-face() mixin simply has to match the path to the font files within our theme. The file extensions will be added by the system on the frontend as needed. (It’s recommended that the five file extensions used by Magento be included to ensure that our fonts will render on all browsers.)

Example:

  • Let’s look at: web/css/source/_typography.less in the Magento Blank theme.
  • On line 13, we see this: @font-path: '@{baseDir}fonts/opensans/light/opensans-300'.
  • This declaration corresponds to the 5 files at: /lib/web/fonts/opensans/light/opensans-300 (.eot, .svg, .ttf, .woff, and .woff2).
  • In a similar way, to define “MyFont” as the default for our site, but without disturbing the core font declarations that we might want to use elsewhere, we could do the following:

    1. Create the following file in our theme directory: web/css/source/custom/_typography.less
    2. Add the following line to our _styles.less file:

    3. Call the .lib-font-face() mixin in our new file, with appropriate parameters:

    4. Redefine the name of the base font family in _theme.less:

    5. Add the font files in the 5 recommended file types in our theme directory at: web/fonts/MyFont.

Note: If you don’t have access to all 5 file types, Transfonter is a great resource for converting between TTF, SVG, EOT, WOFF, and WOFF2 as needed.

Icons

Icons in Magento 2 are implemented using icon fonts, which means they’re vector glyphs that are 100% scalable for all screen resolutions and zoom levels. Colors and sizes can easily be adjusted with CSS properties, just like standard text. Like other elements of the M2 UI library (discussed above in the “M2 UI Library” section), icon variables and mixins can be found in: /lib/web/css/source/lib. The icon fonts will need to be declared and loaded just like other fonts in the system, as outlined in the previous “Fonts” section. For a basic introduction to using icon fonts in Magento 2, I suggest this article by Classy Llama: ‘Icon Fonts in Magento 2: A Foundation’.

Favicons

To add a basic favicon like what most browser tabs display, we can simply add Magento_Theme/web/favicon.ico to our theme directory, and the Magento system will find it automatically. However, favicons these days have many proprietary implementations, so a tool like RealFaviconGenerator is very helpful for getting all favicons for many different systems. I’ll be using their service as an example for how to implement custom favicons in Magento 2 for multiple web clients and platforms:

  1. Go to realfavicongenerator.net.
  2. Click ‘Select your Favicon picture’ and upload an image for your primary favicon (preferably an SVG vector image).
  3. On the next page, go through each of the options to configure and preview how your icon will appear on various platforms.
    • Note that in each case, there’s a ‘Dedicated picture’ option where you can upload a separate image if you don’t like how the main one looks.
  4. Under ‘Favicon Generator Options’ at the bottom, select the option saying “I cannot or I do not want to place favicon files at the root of my web site…”
    • Enter the following path in the text input: images/favicons
  5. Click ‘Generate your Favicons and HTML code’.
  6. On the next page, download the package as instructed in step 1.
  7. Extract the package in your theme directory at: web/images/favicons (instead of using the path given in step 2).
  8. To insert the provided code in the head section of each page:
    1. Create Magento_Theme/layout/default_head_blocks.xml in your theme directory.
    2. Put the given code inside a page/head node, and change each href attribute to a src attribute.

Example:

Note: You may need to remove the color attribute from the link for the safari-pinned-tab.svg, since recent versions of Magento consider that attribute invalid.

See more in DevDocs under ‘Adding custom favicons’.

Other helpful tips

This section is simply a reference of repeatable tips and to-dos that I’ve consistently found helpful when creating new themes. Your method may vary, and some of the tips may become obsolete as Magento is updated, so take it for what it’s worth.

Directories to exclude

Due to its many application layers, code tests, and dynamically generated files, Magento 2 has a lot of areas that aren’t directly relevant to a standard development workflow. I’ve found it helpful to exclude the following project directories in my IDE, to greatly reduce irrelevant search results and excess indexing:

  • /dev
  • /lib/web/css/docs
  • /pub/static
  • /var
  • /vendor/magento/magento2-b2b-base
  • /vendor/magento/magento2-base
  • /vendor/magento/magento2-ee-base
  • /vendor/magento/theme-frontend-luma

Note: In PhpStorm, this can be done in the “Project” Tool Window [⌘1]. Just right-click the directory you want to exclude, hover over “Mark Directory as,” and choose “Excluded.”

Fixes for default styles

There are several default style issues in Magento 2 that tend to show up unexpectedly, so I’ve collected some fixes that I include in themes by default, along with recommended file locations for each (relative to your theme directory).

Styles for Magento_Theme/web/css/source/_extend.less that fix overflow issues and dynamic color/padding issues with the main page column, image gallery, and header:

Styles for Magento_Newsletter/web/css/source/_extend.less that implement default button border radius for the newsletter subscription button:

Styles for Magento_Checkout/web/css/source/_extend.less that fix alignment, button style and spacing issues for minicart, cart page, and checkout:

Styles for Magento_GiftMessage/web/css/source/_extend.less that fix button style and whitespace issues for the custom gift options button:

Changes to default secondary color

Magento Blank is full of places where @color-orange-red1 (or @color-orange-red2) is explicitly set as a highlight color for active elements. If your theme colors don’t happen to include bright orange (which seems likely), but you’ve already done your due diligence during global styling to define things like @active__color appropriately, you may be dismayed to be browsing around your nicely themed site and suddenly find that notorious bright orange color still lurking around. That has happened to me plenty of times during my first few theme builds, so I’ve collected this list of variables to redefine right at the beginning of global styling:

  • @checkout-progress-bar-item__active__background-color
  • @checkout-shipping-item__active__border-color
  • @navigation-level0-item__active__border-color
  • @submenu-item__active__border-color
  • @navigation-desktop-level0-item__active__border-color
  • @submenu-desktop-item__active__border-color
  • @account-nav-current-border-color
  • @collapsible-nav-current-border-color
  • @rating-icon__active__color

(In general, I’ve found that @active__color is the most semantic value for most of these, but your usage may vary.)

One place in particular where the orange color is output directly is for the highlighted image thumbnail on the product detail page. To fix that, you may find the following snippet helpful:

Conclusion

There are a lot of moving parts in the theme process for Magento 2. Even for developers experienced with Magento 1, enough has changed that it can be difficult to understand how to work with the system properly. I hope that this guide provides some clarity and helpful practices to assist you in your next theme build on Magento.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related postsView all
February 12, 2018

Display Configurable Product Price Ranges in Magento 2

Today we’re going to look at the advantages of using configurable products and how we can update how their prices display on Magento 2 product listing […]
January 11, 2018

Selling Bundles the Right Way on Magento 2

Magento has a variety of product types to help you find the right way to sell your products. There are downloadable products for digital items (such […]
November 30, 2017

Creating a Shipping Method in Magento 2

There are existing extensions available for many of the shipping carriers that you may choose to utilize on your Magento 2 site, but what about a […]
Most Recent PostsView all
June 7, 2018

From Brick-and-Mortar To Online: How to Connect with a Niche Audience

As an eCommerce business, every penny counts. If you want to be successful, you have to carefully balance spending money to generate business and the profits […]
May 11, 2018

Impact on Ad Rank and SERP’ real estate

The importance of extensions and how they can increase your SERP’ (search engine results page) real estate and ad rank Different kinds of extensions – One […]
March 27, 2018

How to Create a Payment Method in Magento 2

Sometimes you might need more than the standard out-of-the-box payment method. Perhaps you have an agreement with a credit card processor and a solution for their […]