Getting started with your own Magento 2 Theme
Mar 3, 2017 · 10 minute readCategory: magento2
Prerequisites and Assumptions
- Magento 2.1 installation. See our tutorial here
- Basing from Luma theme
Relevant Technologies
Relevant Magento 2 Concepts
On this page
- New Themeing Concepts in Magento 2
- Module-specific theme files
- Themes
- Static content
- Creating the theme structure
- Folders
- Configuration
- registration.php
- theme.xml
- Applying your theme
- Theme Files
- Templates
- Layout
- Static content (CSS/Images/JS)
- Overriding modules’ view files
- Common Snippets
- Admin Configuration
- Deployment
- Further Reading
New Themeing Concepts in Magento 2
Module-specific theme files
In Magento 2 there is no longer a “base” package or “default” theme. Now, modules contain their own files within their view/(area)/(layout|template|web) folders.
Here are a few examples:
Themes
Themes are a new standalone concept in Magento 2. These themes exist to create their own template, layout, CSS files as a self contained package.
Themes can also specify their own overrides to modules’ theme files, rather than relying purely on file path matching.
Static content
Static resources such as CSS, images and JS files are no longer served from the theme folder itself. Instead they’re published to the pub folder using either symlinks or copies.
Creating the theme structure
Folders
The first step is to create a new namespaced folder path in app/design/frontend/. This would look like app/design/frontend/(Vendor_Name)/(ThemeName)
Magento 2 theme skeleton folders
- app/design/frontend/(Vendor_Name)/(ThemeName)
- (Module_Name)/ sets the theme’s templates and layout for each module, such as Magento_Catalog
- Magento_Theme/ for module-agnostic templates and layout
- layout/ for layout XML files
- template/ for PHTML template files
- media/ contains a preview file
- web/ contains static content for delivering to the browser
- css/
- source/ - less files
- images/
- js/
- css/
- registration.php registers the theme
- theme.xml contains data about the theme such as the name and parent theme
Configuration
registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::THEME,
'frontend/EdmondsCommerce/ThemeTutorial',
__DIR__
);
theme.xml
This file specifies information about the theme, as used in Magento’s admin
<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/theme.xsd">
<title>Edmonds Commerce Theme Tutorial</title>
<parent>Magento/luma</parent>
<media>
<preview_image>media/preview.jpg</preview_image>
</media>
</theme>
Applying your theme
To apply a theme to your store, navigate to the Magento Admin’s Design Configuration page:
- Log into the Magento Admin
- Click Content, and then under Design click Configuration
- Choose your website/store/store view level, and click Edit
- Set your Applied Theme
Theme Files
Templates
Templates are the way to generate frontend HTML content with dynamically generated content.
Magento allows for the ability to add new template engines to take advantage of other templating languages (such as twig) but only comes with PHP and XHTML renderers out of the box.
Bonus fact: the Template Hints are rendered using one of these Template Renderers in Magento\Developer\Model\TemplateEngine\Decorator\DebugHints
Folders
- A module’s template files are contained in its view/(area)/templates folder
- A theme’s template files are contained in its Magento_Theme/templates folder
PHTML Overview
Standard Magento templates are written as PHTML files. This involves a mixture of PHP and HTML, all with a focus on presenting the view-level content. This means business logic should be contained in the Block class and called using its public methods (more on accessing block methods below)
PHTML files look like
<a href="<?php echo $url ?>">
<?php echo $text ?>
</a>
PHTML files use PHP’s alternative syntax for if
, foreach
, while
etc control structures:
<?php foreach($array as $item): ?>
<span><?php echo $item ?>
<?php endforeach; ?>
<?php if($test == "test"): ?>
<span><?php echo $test ?>
<?php endif; ?>
Accessing block methods
As part of the rendering process, Magento makes available a $block
variable which represents an object instatiation of the Block class. As with any other PHP object, you can call its public methods from within the template.
This is a change from Magento 1, where the template was included within the class, and therefore had access to its protected and private methods.
Rendering child blocks
Inserting child blocks is pretty much the same as Magento 1:
<?php echo $block->getChildHtml('block.name'); ?>
Translations and Escaping
Translations can be implemented using the __()
method. Note that this is not a method call on an object:
<span><?php echo __('Learn More') ?></span>
Magento\Framework\View\ElementAbstractBlock
provides public stripTags()
and escapeHtml()
methods. These strip a string of HTML entities and tags respectively.
<h2><?php echo $block->stripTags($_product->getName(), null, true); ?></h2> <!-- the parameters null and true are for string $allowableTags and bool $allowHtmlEntities -->
<span title="<?php echo $block->escapeHtml(__('More Info')); ?>"</span>
Further Reading
Layout
In Magento 2, layout XML works a little differently. The most obvious difference is that each layout handle is its own XML file, so your theme’s layout folder might look like this:
- Magento_Theme/
- layout/
- catalog_product_view.xml
- cms_index_index.xml
- default.xml applies to all pages
- layout/
XML Instructions
Layout XML in Magento 2 seperates its instructions into four groups:
<head>
<body>
<html>
<update>
All but the last one correspond to the HTML elements that contain them. <update>
is simply a way to include another handle’s instructions into the current file.
Magento 2 has a few instructions in Layout XML. Some will be familiar from Magento 1, others are new.
Head Layout Instructions
Title
Sets the page’s title, as used in the window title bar and tab
<title>Page Title</title>
CSS/Script/Link
Adds CSS and Javascript resources to the page. The <link>
tag allows for both IE-conditional comments, and to defer loadinf the script until the page has loaded
<css src="Namespace_Module::css/style.css" />
<script src="Namespace_Module::js/script.js" />
<link src="Namespace_Module::js/script.css" ie_condition="IE 9" defer="defer" />
Meta
Normal meta tags for the page, with key/value pairs
<meta name="content-type" content="text/html; charset=utf-8" />
Body Layout Instructions
Full documentation on body layout instructions
Containers
Containers are a new concept in Magento 2. Containers are intended to represent a part of a page, rather than a block of content. They’re not backed by a PHP Class, and can only create an HTML element.
Attributes can be assigned to the containers, and they can be sorted within their parents.
Examples of standard containers are the header, footer, left, right and content areas.
<container name="container.name" htmlTag="div" htmlClass="class" after="-"></container>
Blocks
Blocks are used to add content to the page, and are backed by a PHP Block class. More on Blocks
<block class="Namespaced\Path\To\Block\Class" name="top.container.welcome"></block>
Move
The <move>
instruction allows for a block to be moved from one block or container to another.
<move element="old.name" as="new.name" destination="destination.block.name" before="-" />
ReferenceContainer/ReferenceBlock
This allows for more content to be added into a block or container
<referenceContainer name="footer"></referenceContainer>
<referenceBlock name="header"></referenceBlock>
More on Blocks
Block Types
There are many types of predefined Block in Magento 2, all contained within Magento\Framework\View\Element
namespace.
- Text is used for displaying simple text on the page. Good for debugging your layout XML
- Text\ListText is extended from Text, this is used to contain a sorted list of other blocks. Blocks added as children to this are automatically rendered
- Messages are their own block type in Magento 2. These represent the “success”, “error” etc banners
- Redirect perform a client-side redirect, if ever that’s desirable
- Template for loading a block with an included template. The template is set with
<block ... template="Module_Name::path/to/template.phtml">
Block methods
Parameters can be called on Blocks’ constructors, as well as their class methods.
To set constructor parameter values, argument
s can be added thusly:
<arguments>
<argument name="logo_img_width" xsi:type="number">220</argument>
<argument name="logo_img_height" xsi:type="number">70</argument>
</arguments>
And class methods by specifying an action
:
<action method="methodName">
<argument name="methodArgumentName" xsi:type="text">value</argument>
<argument name="methodArgumentArray" xsi:type="array">
<item name="array_key" xsi:type="number">1337<item>
</argument>
</action>
Static content (CSS/Images/JS)
These files are stored in the theme’s web subfolder, under their own css, js and images subfolders.
In Developer mode these files are read through symlinks created inside the pub folder. In production though they’re published to the pub folder using the bin/magento setup:static-content:deploy
command.
Images and CSS
A theme’s images and CSS files are stored in the web/images and web/css folders respectively.
LESS
Magento 2 natively supports using CSS preprocessors, and uses Less CSS for its own Luna theme.
Storing Less files
Less files are contained within a modules web/css/source/ folder, with a naming convention that base files are named as file.less
and files to be imported are named with an underscore prefix as _file.less
.
Importing other less files
Because normal Less @import
directives use paths relative to the include path, they’re not aware of Magento’s fallback system. For this reason, other .less files should be included with //@magento_import file.less
- yes, with the //
comment. This means the Less compiler will ignore it, allowing Magento to handle the fallback in its round of compilation.
Referencing images
Image URLs are relative to the web/css folder, so image paths should be url('../images/path/to/image.jpg')
Compiling your Less
There are two ways to compile your less files:
- Using
grunt
commands:grunt exec
sets up symlinks from the files in pub/static to your module/theme’s filesgrunt less
compiles your less to CSS, and then sets up the symlinks to thosegrunt watch
runs a file watcher to track changes to the less files, and compiles the CSS on the fly
- Using
bin/magento setup:static-content:deploy
- Usually used in production
- Copies the files themselves into the pub folder
Variables
Magento provides a set of helpful variables for use in the Less files:
@screen__*
variables for use in@media
queries@icon-*
` variables for a limited set of icons- Navigation variables to customise the category menus
Further Reading
JavaScript
Magento recommends including Javascript as part of templates rather than through layout XML to ensure they run as part of the body.
It makes use of RequireJS to pull in dependencies. These are run with the script tag with a Magento-specific type:
<script type="text/x-magento-init"></script>
Pulling in a Javascript assets
To pull in the module Magento_Configurable’s js/configurable.js file (which will exist in the pub folder):
<script type="text/x-magento-init">
require(["Magento_ConfigurableProduct/js/configurable"], function(Configurable){
// your function body here
});
</script>
To pull in a JavaScript file from your theme, use a relative file path from your theme’s web folder:
<script type="text/x-magento-init">
require(["js/customFile.js"], function(){
// your function body here
});
</script>
Javascript libraries provided by Magento in the lib folder are accessed by name:
<script type="text/x-magento-init">
require(["jquery"], function($){
// your function body here
});
</script>
jQuery Widgets
Not to be confused with UI Components, these extend from jQuery UI components and are useful widgets to use on the frontend. They include accordions, calendars, menus and tabs.
They can be initialised similar to the following in your template file:
<script>
require([
'jquery',
'tabs'], function ($) {
$("#footer-accordion").accordion();
});
</script>
A full list of Widgets can be found at the jQuery Widgets DevDocs page.
Further Reading
Overriding modules’ view files
In Magento 1, overriding other modules’ assets was as simple as matching the file path in your own theme. In Magento 2, things are pretty similar, except you specify which module you want to override.
- In your theme (app/design/frontend/(namespace)/(theme)), create a folder matching the NameSpace_Module you’re overriding
- Create a subfolder for the type of file you’re overriding: templates, layout or web
- Match the original module’s file path, and add your content there
An example would look like:
- app/design/frontend/(namespace)/(theme)/
- Magento_Catalog/
- layout/
- catalog_product_view.xml
- templates/
- product/
- list.phtml
- view.phtml
- product/
- layout/
- Magento_Catalog/
Common Snippets
Templates
URL Generation
<a href="<?php echo $block->getUrl('path/to/page') ?>">Link</a>
Layout
New container
<container name="new_container" htmlClass="container_css_class" htmlTag="div">
<!-- blocks or containers here -->
</container>
New blank template
<block class="Magento\Framework\View\Element\Template"
template="Magento_Theme::path/to/template.phtml"
name="block_name" />
Removing sidebar items
<referenceBlock name="catalog.compare.sidebar" remove="true"/>
<referenceBlock name="view.addto.compare" remove="true" />
<referenceBlock name="category.product.addto.compare" remove="true" />
Removing My Account links
<referenceBlock name="customer-account-navigation-wish-list-link" remove="true"/>
<referenceBlock name="customer-account-navigation-billing-agreements-link" remove="true"/>
<referenceBlock name="customer-account-navigation-downloadable-products-link" remove="true"/>
<referenceBlock name="customer-account-navigation-newsletter-subscriptions-link" remove="true"/>
<referenceBlock name="customer-account-navigation-my-credit-cards-link" remove="true"/>
Admin Configuration
A few aspects of the theme can be user-configured through the Admin. This is found in Content > Design > Configuration > (store) > Edit
- Logo image and dimensions (now includes SVG)
- Title prefix and suffix as displayed in the tab/title bar
- Meta keywords/description as the default if the page doesn’t specify its own
- Favicon image
- Welcome Text which is displayed for logged out users
- Miscellaneous HTML, scripts and CSS inserted before the closing
</body>
and</html>
tags
Image placeholders are configurable at Stores > Configuration > Catalog > Product Image Placeholders
Deployment
When deploying to the server, you should change from Developer mode to Production mode.
With this change in place, resources are served directly from the pub folder rather than through symlinks or the static.php file. To this end you need to ensure your resources are published.
This is accomplished using the bin/magento setup:static-content:deploy
command. This will loop through the themes and modules to deploy their static files to the pub folder.
Remember to set a locale if an Admin user uses a non-default locale, or else they’ll have no CSS in their Admin: bin/magento setup:static-content:deploy --language en_GB