Share icon

What's new?

I’ll list some changes with the theming system of Drupal 8 compared to Drupal 7. You may have got familiar with these in the Drupal 7 version. The bases are still remained, but they have changed in a new form. There may be some points you don’t understand, but don’t worry. I’ll explain them later. Now just take these as bullet points to remember.

  • Your custom theme now goes into a "theme" directory in the root and not in the "/sites/all/themes"
  • .info files now become .info.yml files that use the YAML syntax
  • PHP template engine is replaced by Twig.
  • All theme files are now html.twig instead of .tpl.php.
  • template.php becomes theme_name.theme.
  • Stylesheets (css) and scripts (js) are defined in libraries.
  • Introducing the 2 new base themes - Stable & Classy.
  • CSS coding standards are based upon two very popular conventions SMACSS and BEM.
  • Drupal 8 is responsive out of the box.
  • Breakpoints can be set and used across modules and themes.

Theme Folder Structure in D8

You used to place all of themes, modules, and third-party library assets like Font-Awesome, jquery,... at the sites/all/themes directory in Drupal 7. So in the past, if you want to create a custom theme, you would place it in /sites/all/themes/{custom/}.

File structure in Drupal 8 has changed. Now, the core folder contains all the modules and themes that are used in Drupal core, and other custom or contributed modules and themes will live in the /modules, and /themes respectively.

To create a custom theme, you will need to place it at /themes/{custom}.

A folder structure for a simple theme would look like this

  • theme_name
    • css
    • images
    • js

Let's give you an example of a complete theme folder.


And below are the descriptions of the most common theme files & keys you can find in a D8 theme.

  • .info.yml
    this mandatory file gives information about your theme
  • .libraries.yml
    defines your libraries (mostly your JS, CSS files).
  • .breakpoints.yml
    defines the points to fit different screen devices.
  • .theme
    The PHP file that stores conditional logic and data preprocessing of the variables before they are merged with markup inside the .html.twig file.
  • /css
    where your css files lay. Must be defined in the info, and libraries file to operate.
  • /js
    where your js files lay. Must be defined in the info, and libraries file to operate.
  • /images
    where you theme images are stored. It is a good practice to put images in this folder.
  • /includes
    where 3rd-parties libraries (like Bootstrap, Foundation, Font Awesome,etc) are put. It is a good practice to store them in this folder
  • /templates
    where all your template files (ones that provide html markup of your theme page) are placed.
  • logo.png
    your theme logo if you're using one.
  • favicon.ico
    your theme favicon if you're using one.
  • screenshot.png
    your theme screenshot that will be displayed in the admin/appereance.

The .info.yml file

Drupal will scan the theme directory and search for the file to install your theme. Drupal 8 will look at the .info.yml the same way Drupal 7 looks at .info file. D8 has adopted the Symfony YAML (.yml) format. This is also the format of many programming languages. The pros is that YML uses specific standard which is supported by many libraries.

This is a complete info.yml file of our first D8 theme.

      name: first
      type: theme
      base theme: classy
      description: 'A flexible, colorable theme with many regions and a responsive, mobile-first layout.'
      package: Core
      core: 8.x
      - first/global-styling
        header: Header
        primary_menu: 'Primary menu'
        secondary_menu: 'Secondary menu'
        main_menu: 'Main menu'
        slideshow: Slideshow
        help: Help
        page_top: 'Page top'
        page_bottom: 'Page bottom'
        messages: Messages
        featured: Featured
        breadcrumb: Breadcrumb
        content: Content # the content region is required
        sidebar_first: 'Sidebar first'
        sidebar_second: 'Sidebar second'
        panel_first_1: 'Panel first col 1'
        panel_second_1: 'Panel second col 1'
        panel_second_2: 'Panel second col 2'
        panel_second_3: 'Panel second col 3'
        panel_second_4: 'Panel second col 4'
        footer: Footer

libraries (optional)

A list of libraries (which can contain both CSS and JavaScript assets) to add to all pages where the theme is active.

  - fluffiness/global-styling

libraries-override (optional)

A collection of libraries and assets to override.

libraries-override: #stylesheets-remove(deprecated and will be removed in Drupal 9)
        /core/themes/stable/css/contextual/contextual.module.css: false

Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 theme

Defining a library

Define all of your asset libraries in a *.libraries.yml file in your theme folder. If your theme is named first, the file name should be first.libraries.yml. Each "library" in the file is an entry detailing CSS and JS files (assets), like this:

# first.libraries.yml
  version: 1.x
      css/style.css: {}
    js/script.js: {}

Attaching a library to all pages

Most themes will use a global-styling asset library, for the stylesheets (CSS files) that need to be loaded on every page where the theme is active. It is also possible to do with JS via a global-scripts asset library

# first.libraries.yml (multiple libraries can be added to a libraries.yml file, these would appear below the first-slider libraries added earlier)
  version: 1.x
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
  version: 1.x
    js/navmenu.js: {} 

Attaching a library via a Twig template

You can attach an asset library to a Twig template using the attach_library() function in any *.html.twig, file like so:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>


Attaching a library to a subset of pages

In some cases, you do not need your library to be active for all pages, but just a subset of pages. For example, you might need your library to be active only when a certain block is being shown, or when a certain node type is being displayed.

A theme can make this happen by implementing a THEME_preprocess_HOOK() function in the .theme file, replacing "THEME" with the machine name of your theme and "HOOK" by the machine name of the theme hook.

For instance, if you want to attach JavaScript to the maintenance page, the "HOOK" part is "maintenance_page", and your function would look like this:

function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'first/first-slider';

You can do something similar for other theme hooks, and of course your function can have logic in it — for instance to detect which block is being preprocessed in the "block" hook, which node type for the "node" hook, etc.

Important note! In this case, you need to specify the cacheability metadata that corresponds to your condition! The example above works unconditionally, so no cacheability metadata is necessary. The most common use case is likely where you attach some asset library based on the current route:

function fluffiness_preprocess_page(&$variables) {
  $variables['page']['#cache']['contexts'][] = 'route';
  $route = "entity.node.preview";
  if (\Drupal::routeMatch()->getRouteName() === $route) {
    $variables['#attached']['library'][] = 'first/node-preview';

Add new comment

Restricted HTML