$mash@bradmash.com: Technology Enthusiast & Full Stack Web Developer

How Diablo II captivated a generation.

The Diablo II: Lord of Destruction Hero Characters

What was it about Diablo II that captivated an entire generation of pc gamers? Even as of the time of this writing (2019), logging into the U.S. East servers reveal a few thousand active players at any given time, with hundreds of public games to join. Now a legend, Diablo II has become the defining video game to showcase how an action RPG hack ‘n slash is supposed to be done.

Blizzard North

Blizzard North

Blizzard North was originally a third party development team (Condor) and were not originally a “part of the [Blizzard] family” according to Diablo II senior producer Bill Roper. The company was headed by president at the time David Brevik, and vice presidents and brothers Erich Schaefer and Max Schaefer. However, the company was eventually acquired by Blizzard and became known as Blizzard North (as their office resided in northern California). Brevik ran the company from 1993 to 2003, during which time Blizzard North was virtually sovereign from their parent company, Blizzard.

Low Level of Entry

Logitech Mouse

CLICK! CLICK! The average computer user can install Diablo II and start slaying demons. Every action in the game can be accomplished solely with a mouse. The pc hack ‘n slash genre was born with Diablo I, but perfected with Diablo II’s mindless clicking to vanquish demons. According to Roper this was by design to make the Diablo trilogy as accessible as possible to players.


This is Diablo II’s Battle.net interface.
An improvement over Diablo I’s chunky and dry interface.

Diablo I was released at the end of 1996 and showcased for the first time Battle.net. D1’s interface was clunky compared to its new interface in Diablo II. But at the time of Diablo’s first debut, Battle.net was a hit. Gamers played cooperatively (pve), deathmatch/team deathmatch (pvp), and chat in-game over a standard TCP/IP protocol Blizzard North developed and unleashed Battle.net to the world at the very end of 1996.

Once Battle.net was upgraded with Diablo II, all hell broke loose. A clean way to find, create, and search for public and private games made the online community surge.

According to Roper, the idea for Battle.Net came about in E3 of 1996 and a ton of work went into perfecting Battle.net both from its inception, and its later update with Diablo II.

Gameplay & Story

Diablo has always put an emphasis on developing its story through gameplay. Diablo II’s gameplay and story has been enhanced by its state of the art (at least for the time) integrated cinematics. Showing the wandering traveler on his journey against the Prime Evils.

Diablo put the emphasis on the player to develop their hero character in every respect. Taking a metric ton of elements from Dungeons & Dragons, the player had a few options when it came to developing their character. As one would advance in the game, their character would level up. As their character would level up, stat points and skill points were allotted to the player to spend for reaching their new rank. This allowed the player to “build” their character.

The Classes

Diablo 2 Characters

Rather than get lost in details, I recommend just picking up a copy of Diablo II with the expansion (Lord of Destruction) and playing the game. Diablo II came with 5 playable character classes (Paladin, Barbarian, Necromancer, Sorceress, and Amazon), and two additional character classes in the Lord of Destruction expansion (Assassin and Druid).

Necromancer in Diablo II
A necromancer in Diablo II showing off his legion army of skeleton warriors.

As time went on, various “builds” started to become popular. The amount of builds on the internet were in the hundreds and possibly thousands, some cookie cutter, while other builds where experimental in nature. Diablo II perfected giving the player enough choice so that decisions actually mattered, but enough limitation so that the player could always see direction with their build.

The Loot

Diablo 2 Loot

Players would grind for hours to level their character. But with the grind came loot. Diablo II’s loot system was implemented with granularity in respect to how often certain items and types of items would drop, which enemy types could drop certain types of items, which locations could drop which items as well as other factors such as the players current “magic find” attribute value. The higher a player’s “magic find”(mf for short) the higher chance they had at finding better items.

In Diablo II, players who joined public games via the Battle.net service, would find that fast clicking paid off when it came to snatching loot. Unlike many arpg’s and mmo’s today, when loot “dropped” it was first come first serve. In other words, if an item dropped on the ground, whoever picked the item up first got the item and the other players were out of luck.

This type of loot system arched Diablo II’s player community to be somewhat stingy. People would steal items, and generally it was a good idea to not trust anyone on battlenet unless you actually knew them in real life, or had played with them for an extended period of time (months to years).


With the expansion Lord of Destruction came runewords. Items which were socketed could have runes injected into them. If runes of specific types were put into socketable items, in specific orders, runewords would be created.

Ebotdz Runeword

Recipes were a source of mystery, but to the general public’s knowledge, all runewords have been discovered and cataloged by the writing of this post.

The Secret Cow Level

The legend of the Secret Cow Level is all to familiar to avid fans of Diablo. Just get yourself a Town Portal Tome and Wart’s Leg, and combine the two itemswith the Horadric Cube in Act 1 Rogue Encampment… and slay some cows.

The Secret Cow Level


This article could be quadruple its size and still not cover everything. Many details have been missed in terms of what makes Diablo II such a great video game. However, the intent of this article was to remain as objective as possible. What made Diablo II such a success? Blizzard North, low level of entry, Battle.Net (revamp), the new and refined classes, the new loot, runewords, and the Secret Cow Level (don’t kill the king!).

DOOM Eternal Pre-Release Review

DOOM Eternal Game Artwork

I have played Doom since I was about 5 years old. I can remember sitting on my grandpa’s lap, he would make the doom slayer run and I would do the shooting. Growing up I loved Doom 1 & 2! The second installment (Doom II) in the series is what I played the most growing up. I still occasionally install Doom 1&2 and get my demon slaying on. From what I can tell, DOOM: Eternal is going to be a return to nostalgia, with Hell on Earth crazy as ever. Let’s take a look.

DOOM: Eternal Returns to More Classic Enemies

This may be the most exciting thing about DOOM: Eternal for me. From what I can tell, the Id team is really doing their best to get these models and concepts to feel right. They’re keeping the nostalgia alive by doing their best to stay true to their classic counterparts, but also revamping them to be bigger than life.

Let’s take it from square one. You want a modern take on your early Id Doom days? Let’s compare the new concepts to the models from days of old. However, below is not an exhaustive list of all demons in the game, or even returning demons from 2016 (some from 2016 return with slight modifications, but from my viewpoint did not change enough to warrant mentioning here). All of the below concepts and models are brand new and make a first appearance in DOOM: Eternal


The zombieman is back!

DOOM Eternal Zombie
Top left classic Doom zombie, main image DOOM: Eternal zombie.


The main difference seems to be the new cannon sitting on top of the Arachnotron.

DOOM Eternal Arachnotron
Bottom right classic Doom II Arachnotron, main image DOOM: Eternal Arachnotron.


The arch-vile is back!

DOOM Eternal Arch-Vile
Left classic Doom II Arch-Vile, right DOOM: Eternal Arch-Vile.


The mancubus is back!

DOOM Eternal Mancubus
Top right classic Doom II Mancubus, main image DOOM: Eternal Mancubus.

Pain Elemental

The Pain Elemental is back! Personally, strictly in terms of how this demon was reincarnated, to me, looks seriously awesome!

DOOM II Pain Elemental
Classic Doom II Pain Elemental.
DOOM Eternal Pain Elemental
DOOM: Eternal Pain Elemental.

Final thoughts on Classic Enemy Reintroduction

From what I can see, the five brand new enemies to the new DOOM titles (2016+), as seen above, really look nothing short of amazing. It seems id has really taken time to try and keep the retro-nostalgic feel to these enemies while updating them contextual to DOOM: Eternal. I hope you’re as excited as I am to see these brought back!

DOOM: Eternal Invasion Mode

From what I have seen, not much has been revealed about this play mode. The concept is exactly as it sounds, you can invade the single player campaign of people around the world and try and kill the doom slayer. You can choose between a select demon set. This is a 2 versus 1 style of deathmatch.

If executed well, I think this is a great change to the franchise, and offers players a fresh experience on DOOM multiplayer.

In conclusion, you can at the end of this video if you start the video at 16:40 (I’ve gone ahead and done that for you if you just press play) that two players join before the gameplay footage stops. What a teaser!

DOOM: Eternal Level Design

From what we get in early gameplay footage, it seems that the overall level design for DOOM: Eternal is much more open as compared to the level design in DOOM 2016. The latter did feature open environments, but this seems like a staple in DOOM: Eternal. This probably stems from the fact that it’s set on Earth just as classic Doom II. At the very least, this is evidenced by more vertical mobility in the game, featuring a new grappling hook.

DOOM: Eternal Movement & Grappling Hook

DOOM: Eternal Grappling Hook.
The new grappling hook featured on the the bottom of the double-barrel shotgun (super shotgun).

The grappling hook mechanic in DOOM: Eternal is another staple to the franchise as a whole. The vertical mobility it offers for doom guy to slay enemies are endless. Chaining together grapples can lead to some serious air time! Combined with double jumping and wall climbing, the movement in DOOM is better than ever.

Let’s not forget about the new strafing! The quick strafes that doom guy can now perform look amazing. This will offer players better defense from escaping close calls with enemies. Combined with the other new movement mechanics the overall gameplay in DOOM: Eternal compared to DOOM 2016 will be eons apart from each other.

DOOM: Eternal New Guns and Weapons

We won’t spend time looking at guns that have changed only visually, or that are not totally new to the franchise. With that out of the way, check these out.

DOOM: Eternal – Flamethrower

If they came from Hell, they should feel right at home.

DOOM: Eternal Flamethrower
The Flamethrower.

DOOM: Eternal – Heavy Cannon

Although very similar to DOOM 2016’s heavy assault rifle, it is actually a brand new gun and according to some sources is presumed to replace the heavy assault rifle. There are two additional modes the gun can be used in: “Precision bolt” for sniping; and mini-missiles that alternate when fired off of both sides of the weapon.

DOOM: Eternal Heavy Cannon
The Heavy Cannon.

Doom: Eternal – Doom Blade

Ready for some new and awesome glory kills? Then get ready for the doom blade.

DOOM: Eternal Doom Blade
The doom blade.

DOOM: Eternal – Shotgun

Alright, so… yeah it’s not a new weapon. But what kind of DOOM review doesn’t talk about the shotgun?! The one thing to mention here is that in DOOM: Eternal there is a mode for the shotgun that makes it an automatic weapon. Nuff said.

DOOM: Eternal Shotgun
Long live the shotgun.

DOOM: Eternal – Grenade Launcher

This is a presumption that some sources have talked about but has not yet been confirmed. That is to say, this is not the same weapon as the Rocket Launcher.

Final Thoughts on Teaser Trailers

Having watched the trailers a few times, I just want to play the game. At the beginning of this article I mentioned that my favorite Doom game ever is Doom II: Hell on Earth. Knowing that this is the second DOOM game in the new revamped DOOM titles… it makes me smile knowing it takes place on Earth. Just as Doom II: Hell on Earth was Doom I with more content, it seems like DOOM: Eternal is DOOM 2016 with more content, and I’m totally okay with that. The music still sounds great from what we hear in the trailers – thank you Mick Gordon. Speaking of Mick Gordon, if you missed this great GDC talk he gave on how he made the original music for DOOM 2016 you should check it out.

Mick Gordon, music producer for DOOM 2016 talks about how he worked on the project.

It seems to me that this installment, like the last, would make the 90’s id team proud. I can see John Romero and John Carmack smiling at its release, although Carmack will likely not say a word about the release ( https://arstechnica.com/tech-policy/2017/03/john-carmack-zenimax-owes-me-22-5m-as-part-of-2009-id-software-deal/ ). It will be interesting to see if the others comment on it. Romero on the other hand is a vocal dude, and was for DOOM 2016, which I don’t really see changing for this release.

Official DOOM: Eternal Story Trailer E3 2019

Your thoughts?

What are your thoughts on DOOM: Eternal up to this point? What excites you the most about this upcoming release? How do you see this release comparing with the last? What are your thoughts on the Invasion multiplayer mode? Let me know your thoughts in the comments!

Drupal 8 theme: How to develop a custom theme.

Drupal 8 Logo

Whatever your use case is for developing a custom theme in Drupal 8, the PHP Symfony, Twig, and markup goodness Drupal 8 offers developers is great. Let’s cut to the chase and get started.

You can easily scaffold a theme using drupal console with the drupal generate:theme [options] command found at https://hechoendrupal.gitbooks.io/drupal-console/en/commands/generate-theme.html. In this guide, we will be creating a custom Drupal 8, manually generating the files as they are needed. However, do not overlook this command for future use. After you go through this guide and understand what composes a theme, I recommend for future projects using this command.

Create a Drupal 8 theme directory.

The first thing you will need to do is create a directory for your theme. Generally speaking, best practice is to create the directory structure as follows: docroot/themes/custom/YOURTHEMENAME

In this example, we will name our theme directory as docroot/themes/custom/artistCamp

A quick note on yml files.

Anytime you see indentation in yml files, the indentation are important and are only 2 spaces. That is to say, if something is indented twice, it equates to 4 spaces. Improper indentation of yml files will make things not work properly. And generally speaking, improper syntax at all will break things.


This file is required. Once you have your theme directory created, you will need to create the *.info.yml file. In our case, we will name this file artistCamp.info.yml. This file is a configuration file which will register our theme with our Drupal 8 install. In this file there are a few required key/value pairs as well as many optional key/value pairs that we define as metadata to describe our theme.

Required *.info.yml key/value pairs.

The required keys are name, type, and core. For our example we will define a few other useful keys. Below you will see the contents of artistCamp.info.yml.

*.info.yml file used for registering your Drupal 8 theme with the system.

Some of the keys are self-evident such as “description” but let’s elaborate on a few of the other keys.

base theme: Your theme can derive from another theme and according to drupal.org:

“Sub-themes are just like any other theme, with one difference: they inherit the parent theme’s resources. There are no limits on the chaining capabilities connecting sub-themes to their parents. A sub-theme can be a child of another sub-theme, and it can be branched and organized however you see fit. This is what gives sub-themes great potential.”

screenshot: The “Appearance” admin user-interface (/admin/appearance), when you are selecting a theme to use for your website, will show the image you specify via the path provided to the screenshot key.

regions: This is very important metadata that creates reference-able regions that will become the main parts/sections of your theme. Each region renders as its own encapsulated black box. Regions are reference-able via their unique hook. These hooks are essentially IDs that reference your regions for theming and pre-processing. More on this later. The Structure -> Block Layout admin user-interface displays the custom regions you define, so that administrators can add blocks to those regions as needed.

libraries: A reference to a defined Drupal 8 “library” (that is defined in *.libraries.yml more on this later) applied to every node (in Drupal jargin a node is a page) using this theme.


Include a theme as it is best practice to have one. If you create it blank, make sure to at least include the <?php at the top of the file, and including a comment like /* Stub */ may be nice as well.

This file allows you to implement a list of life-cycle hook functions associated with your theme such as HOOK_preprocess_page. There are a plethora of contextual hooks you can implement, of which you can check out on the drupal.org website. However, for this file, if we were to implement HOOK_preprocess_page for the artistCamp example, the function name would be, artistCamp_preprocess_page. This function allows us to prepare variables for the page.html.twig file. Drupal inherently knows when to run this function. The equivocation of this function prepares our variables for the page.html.twig template to use. Drupal 8 accomplishes this with its event system runs implemented life-cycle hooks at the proper times.

Preprocessing Drupal 8 theme variables.

As we saw in page.html.twig, our Twig templates can be passed preprocessed variables. That is to say, any Twig template can have custom, defined variables at their disposal if we set them up properly, one way being through preprocess functions. Let’s take a look at the artistCamp_preprocess_page function defined in the artistCamp.theme file.

hook_preprocess_page function used for preparing variables for use in your Drupal 8 theme twig templates.

It is worth noting before commenting on the above function definition that to create custom variables for use in Twig templates via their preprocess function counter-part, that you create them in the following format:

$variables['foo'] = "bar";

So, we create a custom variable called ‘current_route’ to use in the artistCamp page.html.twig template. The route the current user is requesting becomes the value of ‘current_route’. This function only runs on nodes that use the page.html.twig template, which, for most sites, will be used on every page.

Next we will look at the html.html.twig file, however I would urge you quickly scroll past it and look at the page.html.twig file to fully follow this example. To clarify, the ‘current_route’ variable determines the markup generated to the client requesting the web page. I recommend to still read the html.html.twig section.


This twig template serves as the entry point for ultimately rendering all of your templates. It always runs on page loads that are uncached. Let’s take a look at the artistCamp html.html.twig file.

* @author Brad Mash, Software Engineer
* @file
* Theme override for the basic structure of a single Drupal page.
* Variables:
* - logged_in: A flag indicating if user is logged in.
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
*   May contain one or more of the following:
*   - title: The title of the page.
*   - name: The name of the site.
*   - slogan: The slogan of the site.
* - page_top: Initial rendered markup. This should be printed before 'page'.
* - page: The rendered page markup.
* - page_bottom: Closing rendered markup. This variable should be printed after
*   'page'.
* - db_offline: A flag indicating if the database is offline.
* - placeholder_token: The token for generating head, css, js and js-bottom
*   placeholders.
* @see template_preprocess_html()
set body_classes = [
logged_in ? 'user-logged-in',
not root_path ? 'path-frontpage' : 'path-' ~ root_path|clean_class,
node_type ? 'page-node-type-' ~ node_type|clean_class,
db_offline ? 'db-offline',


{% set frontClass = ""  %}
{% if current_route == "view.frontpage.page_1"  %}
{% set frontClass = "frontpage" %}
{% endif %}

<!DOCTYPE html>
<html{{ html_attributes.setAttribute('class',frontClass) }}>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX"></script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'UA-XXXXXXXXX');

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Google Tag Manager -->
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
<!-- End Google Tag Manager -->

<link rel="preload" as="script" href="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">
(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "ca-pub-XXXXXXXXXXXXX",
enable_page_level_ads: true
<head-placeholder token="{{ placeholder_token }}">
<title>{{ head_title|safe_join(' | ')|trim('| ') }}</title>
<css-placeholder token="{{ placeholder_token }}">
<js-placeholder token="{{ placeholder_token }}">
<body{{ attributes.addClass(body_classes) }}>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<section aria-label="Skip to main content.">
{# Using the open source implementation found at: https://accessibility.oit.ncsu.edu/it-accessibility-at-nc-state/developers/accessibility-handbook/mouse-and-keyboard-events/skip-to-main-content/ #}
<a href="#page-instance-fluid" class="skip-main">
{{ 'Skip to main content'|t }}
{# Do not remove these three: page_top; page; page_bottom; ever! #}
{{ page_top }}
{{ page }}
{{ page_bottom }}
<js-bottom-placeholder token="{{ placeholder_token }}">

You can see that the main HTML elements are here, html, head, body.

Rather than go into great detail about everything going on in this file, we will comment on some high-level things. First notice that we can attach libraries directly to our template with a single twig function:


Libraries will be discussed in the *.libraries.yml section below. However, what this is doing is taking all of the resources listed in the ‘artistCamp/global-header’ library (mainly CSS and JavaScript references), packaging them up, and applying them to the final rendered page markup this template produces.

Finally, towards the bottom of the file you will notice the following:

{{ page_top }}
{{ page }}
{{ page_bottom }}

These are variables which reference global parent regions (not the regions we defined, these regions, particularly the page region, hold our custom defined regions we defined in our *.info.yml file). If you’re curious why page_top and page_bottom are not accessible in Block Layout you can see the following link, https://api.drupal.org/api/drupal/core!modules!system!system.module/function/system_system_info_alter/8.2.x. But in short, these are system administered regions.


If you’re following, you should see that whatever is output via this file will be between the <body>…</body> tag since {{ page}} was output there as we saw when we looked at the html.html.twig file.

This is the default template that all themes use if no other twig template overrides their precedence (more on twig hook theme precedence later). To clarify, if your theme includes all of the files this tutorial has outlined up to this point, as well as this file, this is the file that is basically going to generate all of the markup for your website no matter what page you’re on.

Let’s take a look at the artistCamp page.html.twig file, and keep in mind that we preprocessed this twig template via out *.theme file’s artistCamp_preprocess_page function which made the ‘current_route’ variable available.

* @author Brad Mash, Software Engineer
* @file
* Artist Camp Theme File
* page.html.twig
* Implements Bootstrap 4.1.x
    <!-- Artist Camp Developed by Brad Mash. -->
    <div id="global-container" class="global-container">
{% include directory ~ '/templates/includes/header/header.html.twig' %}
<div id="global-content-container">
<div id="content" class="content">
<div class="top-fluid-container">
<div id="top-fluid" class="top-fluid">
{{ page.top_fluid }}

{% set containerized_routes = ['user.login', 'user.register', 'node.add','node.add_page','entity.user.edit_form','comment.admin','view.files.page_1','view.message.page_1']  %}
<div id="page-instance-fluid" class="page-instance-fluid">
<main id="main">
{% if current_route in containerized_routes %}
<div class="container">
{{ page.content }}
{% else %}
{{ page.content }}
{% endif %}

<div class="bottom-fluid-container">
<div id="bottom-fluid" class="bottom-fluid">
{{ page.bottom_fluid }}
{% include directory ~ '/templates/includes/footer/footer.html.twig' %}

{# {% include directory ~ '/templates/includes/footer/footer.html.twig' %} #}

Again, rather than comment on every part of the file, let’s consider the more interesting parts. The first thing we want to point out is the use of our custom defined regions such as {{ page.top_fluid }}

If needed can theme the top_fluid region with a twig template. We will not spend time doing this in this tutorial. However, Drupal will provide theme hooks for this region which can be themed and preprocessed as needed.

**Note: This may be a good point in time to say that twig templating in Drupal 8 is very granular in scope. For example, a form on a page can be twig templated and preprocessed. But then, each input of the form themselves can be as well! Twig templating is a game of inception.

Drupal 8 Twig Logic

Next consider this snippet of twig markup found in the artistCamp.page.twig file:

{% set containerized_routes = ['user.login', 'user.register', 'node.add','node.add_page','entity.user.edit_form','comment.admin','view.files.page_1','view.message.page_1']  %}
<div id="page-instance-fluid" class="page-instance-fluid">
<main id="main">
{% if current_route in containerized_routes %}
<div class="container">
{{ page.content }}
{% else %}
{{ page.content }}
{% endif %}

It’s easy to see we have a small piece of logic that checks our custom defined and available ‘current_route’ variable, and depending on its value either outputs the {{ page.content }} region in a container or not.

Finally it’s worth mentioning that you can include twig templates inside of other twig templates using the following syntax as we see in this file including the footer twig template:

{% include directory ~ '/templates/includes/footer/footer.html.twig' %}


This file contains library definitions which can reference internal and external resources such as CSS and Javascript, and houses those defined resources under a custom name/ID that you choose. Let’s take a look at the artistCamp.libraries.yml file:

#Global for all pages
  version: 1.0
  header: true
    templates/includes/css/global/temp.css: {}
templates/includes/css/global/base.min.css: { minified: true }
templates/includes/css/global/layout.css: { minified: true }
templates/includes/css/global/layout.min.css: { minified: true }
templates/includes/css/bootstrap/bootstrap-grid.min.css: { minified: true }
templates/includes/css/custom-dropdown-menu.css: {}
https://cdnjs.cloudflare.com/ajax/libs/flickity/2.1.2/flickity.min.css: { minified: true }
https://unpkg.com/ionicons@4.2.2/dist/css/ionicons.min.css: { minified: true }
https://cdnjs.cloudflare.com/ajax/libs/plyr/3.4.6/plyr.css: {}
templates/includes/css/global/component.min.css: { minified: true }
templates/includes/css/global/header.min.css: { minified: true }
templates/includes/css/global/footer.min.css: { minified: true }
https://use.typekit.net/hkv5mea.css: {}
https://fonts/googleapis.com/css?family=Encode+Sans+Condensed: {}
 #templates/includes/css/global/print.css: { media: print }
    templates/includes/js/custom-dropdown-menu.js: {}
    templates/includes/js/temp.js: { weight: -5, minified: true }
    templates/includes/js/global.min.js: { weight: -5, minified: true }
    https://cdnjs.cloudflare.com/ajax/libs/plyr/3.4.6/plyr.polyfilled.min.js: {minified: true}
    https://cdnjs.cloudflare.com/ajax/libs/flickity/2.1.2/flickity.pkgd.min.js: {minified: true}
    - core/jquery
    - core/jquery.once

    templates/includes/js/custom-plyr-implementation.js: {}

  version: 2.1
  header: true
templates/includes/css/global/404.min.css: {}

If you recall, in the html.html.twig file we had:


These two lines applied the resources defined in the libraries file to our template, and ultimately applies them to all rendered pages.

Advanced Theming

As mentioned in this tutorial, theming in Drupal 8 is very granular. That is to say, virtually any component on your webpage not only is likely theme-able, but will have dedicated theme hook names (suggestions) associated with it so that you can implement the proper files to target that component. If you have twig debugging turned on for you site, you can go to any page in your site, open developer tools, and in the markup HTML comments will be present that show you the twig theme suggestions. To clarify, this means that if you need to theme or preprocess a particular component the HTML comment will basically tell you what file you need to create so that you can target the component in question. Read more on this topic at https://www.drupal.org/docs/8/theming/twig/debugging-twig-templates.


Once you have implemented these files, you can go to yoursite.com/admin/appearance and install and set your theme as active.