Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Jul 15 2021
Jul 15

The long answer:

  1. cd to /web/themes/contrib/localgov_base
  2. run bash scripts/create_subtheme.sh
  3. done

The details are available in this short video:

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

Jun 02 2021
Jun 02

Theme settings are simple and powerful, here's me figuring out how to create one.

Creating theme settings have so many uses. For example, you might have a sub-theme that wants to set a theme setting for the main/accent colour, and then use CSS variables to pull that into your theme - simple. Or, in my case, I want to create a base theme that is styled with CSS and JS, but allow sub-themes to switch off those libraries and just use the HTML from our templates.

Here's how:

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

May 13 2021
May 13

Drupal currently supports IE11, and will do until Drupal 10 in about a year's time. Here's my proposal for how to support IE11 in a distribution without holding back from modern CSS.

Drupal will support IE11 until about this time next year. We have been seeing many clients say to us that they are happy to drop support for IE11 if it means they can have a better website. However, some organisations (government, etc) still use IE11 internally as their main browser, so those users need a website that works and is a pleasure to use. However, often the only users of these websites in IE11 are internal users - editors updating content - and not "visitors" to the website who are mostly using mobile browsers on their phones or modern browsers like Firefox, Edge, and Chrome on their computers.

Within LocalGov Drupal, we aim to support IE11 as long as Drupal does. However, I don't want us in a position this time next year needing/wishing to rewrite our frontend in modern CSS after we drop IE11 support. Here's my proposal to give IE11 users a really good experience via a default theme (like Wordpress does for mobile sites hosted on Wordpress.com) but uses progressive enhancement to supplement how the site works for visitors using modern browsers.

If we take the approach in this video, it means that all CSS properties are variables (fonts, line-heights, spacing, colours, etc) and then any council can use our base theme but brand it to their own brand guidelines simply using a variables.css file to override the base CSS variables. Since variables are calculated at run time, we don't need to recompile the theme to each time we make a simple change. Why is this so great/sustainable/scalable? It means sub-themes are very small, but also means we can create theme settings so editors can set some of these variables themselves (e.g. setting background colour via the CMS for Christmas, or when a notable person has passed away) all without needing to contact developers.

But won't the site look different in IE11 to other browsers? If you site uses the base theme, it will look the same in all browsers. If you site uses the base theme but extends it, then it will look like the base theme in IE11 but your branded theme in other browsers. Remember, sites already look different in different browsers - everything stacks in one column on phone browsers buy is side-by-side on desktop browsers, etc. Different does not mean bad.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

May 13 2021
May 13

We often end up with a lot of templates when theming, but we should also remember to create "sensible defaults", so before you start theming specifically your site doesn't look broken.

Writing sensible defaults means that the dev we might perform or the CSS we might write for a new feature/component is additive, and you do not need to reset libraries and/or write code to overwrite other code. Here's an outline of creating sensible defaults for our region template within the new localgov_base theme - region.html.twig.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

May 11 2021
May 11

Here's an example of where "less is more" sometimes. I reckon we can make the video paragraph type in LocalGov Drupal simpler by just removing the template.

When looking recently at the template for the video paragraph type in LocalGov Drupal, I thought, "hang on, is this necessary?". As it turns out, we have a template for it, but I'm not sure it's needed.

One of the reasons is must be there however is because we have set a template suggestion for it in the localgov_subsites_paragraphs.module file, so if it's removed we will get a Twig error about an undefined template.

Let's go through this template implementation step-by-step to show how sometimes less is more.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

Apr 29 2021
Apr 29

Currently LocalGov Drupal relies on Bootstrap for its grid system. I think we can achieve a really nice grid system with about 30 lines of CSS. Let's see!

When it comes to CSS frameworks, I really don't like using them. I find them bloat and opinionated. I also think they go against basic principles of scaling a web project, because each dependency we include forces that dependency on all users.

If there's something that we can do in a few lines of code that means we don't need to depend on a framework, I think we owe to to ourselves to investigate it. Here's my proof-of-concept of a grid system based on the GOVUK and NHSUK design systems' grid systems. It's only about 30 lines of CSS.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

Apr 29 2021
Apr 29

I've been doing a lot of contribution to LocalGov Drupal. Here's my thought process when refactoring some code for the alert banner module.

LocalGov Drupal is the distribution for local councils in the United Kingdom. When reviewing the alert banner module, there were a few small issues that niggled me with the alert banner HTML and CSS. I wanted to make sure the HTML followed BEM naming conventions, and then the CSS the same. This means the CSS is easier to read, and relies less on nested selectors.

There was also a tiny issue with hover states on some of the buttons.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

Apr 28 2021
Apr 28

Ok, maybe the title is a bit ambitious, but here's a little proof-of-concept video of any idea for sub-theming I have been working on.


  1. Create a theme.
  2. Use CSS variables for as many CSS properties as you can - spacing, line-height, font, etc.
  3. Create a sub-theme.
  4. Create a library in that sub-theme which just has a CSS file.
  5. Use that CSS file to set values for the variables in the sub-theme.

E.g. in the base theme, you could have padding on top and bottom of header. This could be a variable called --section-spacing-v (as in, section spacing vertical) and set to 2rem. Then in the sub-theme you could set that variable to 4rem. Continue for any other variables you want to override - fonts, line-height, etc.

For added points, you could take these variables and save them as theme settings, so site builders can set them.

I've started streaming some of my work, especially if it's contributions to open source, on twitch if you'd like to follow along/subscribe. As it turns out, Twitch deletes your videos after 14 days, so I have also been uploading them to YouTube. Feel free to subscribe.

Dec 23 2020
Dec 23

Here are two short, related videos demoing how I create a view mode component, use that view mode in a views list in Patternlab, and then integrate both with Drupal.

In this first video, we will look at creating the view mode pattern and then integrating that with Drupal.

In the second video, we will see how to create a views listing in Patternlab and integrate that with Drupal - in essence, we just clone the views templates into Patternlab, so the heavy lifting is done for us, no need to recreate the wheel.

Let me know in the comments what other videos you might like to see.

Dec 10 2020
Dec 10

When every tutorial for syntax highlighting in Gatsby talks about highlight.js markdown but you have a Drupal website, you need to write your own for posterity. Here's mine.

This website uses the very helpful CKEditor CodeSnippet module to provide syntax highlighting. That in turn uses highlight.js. However, while investigating using Gatsby for the frontend of this website, I came across the headache of adding code highlighting to the site, when my code is just a string in the body field. Here's my solution ... if you have a better one, let me know in the comments.

Create custom utility function for highlight.js

  • Add highlight.js to your repo npm install highlight.js
  • Create a file at /src/utils/highlightCode.js.
  • Import the library and the CSS style you want to use
  • Set the languages you want to load (we don't need to load all of them for performance reasons)
  • Create a function in this file that finds every instance of your code element and loop through it to let highlight.js work on each instance. That file will look something like this:
import hljs from 'highlight.js'
import 'highlight.js/styles/monokai-sublime.css'

  languages: [

export default function highlightCode() {
  const codeBlocks = document.querySelectorAll('pre > code')
  codeBlocks.forEach(codeBlock => {
    if (typeof codeBlock === 'object') {

Use the useEffect() hook to apply this to our template

In my case, I wanted to apply this to the article template, which means attaching it to the

but by the time Gatsby has grabbed and rendered that, it's too late. Instead, we add our useEffect() before we render our article - this is basically the hook_preprocess_node() of React! That's looks like this:

// import React, etc, etc

import highlightCode from '../../utils/highlightCode'

export default function SingleArticlePageTemplate({ data }) {
  useEffect(() => {
  return (

        // Stuff before body field (title, image, etc)


// Stuff after body field (tags, etc) </Layout> </> ) }

It's pretty simple, hopefully it will help you/save you some time Googling.

Jul 29 2020
Jul 29

I had a very large key_value_expire table, even though Honeypot module had been uninstalled.

When investigating why I had such a large database on mark.ie (about 50MB - not huge, but a lot for a personal website) this evening, I noticed that the key_value_expire table in the database was about 40MB (after clearing cache). And that most of the entries in it (over 160,000) were for honeypot_time_restriction.

As it turns out, honeypot had been putting a time to expire of Jan 2038, so the table was just getting bigger and bigger. Then I did some Googling and found this: Honeypot key value entries never expire (effectively). That issue has been fixed for newer versions of honeypot, but all the old entries before that update are still in the DB.

There's another issue - Write update hook to clean up old honeypot_time_restriction values - to clean up all the old entries, but it's only at RTBC at the moment, and when it gets into the Honeypot module, it could take a long time for the update hook to run depending on how many entries you have in the table.

For my set up, I just ran DELETE FROM key_value_expire WHERE collection = "honeypot_time_restriction"; directly on the DB. It only took about 10 seconds to delete the 160,000+ rows, and reduced that table from about 40MB to 3MB.

It might be worth checking this on your site to try reduce your DB size.

Oct 03 2019
Oct 03

Someone asked in Slack today how to print the URL of the node that a paragraph is on. I was up to the challenge.

First off, you can do this with php in your .theme file quite easily, but I like to keep my template items in my templates.

Here's the code I used to first get the node id, then the node title, and then create a link from these two pieces of information.

{% set parent = paragraph._referringItem.parent.parent.entity %}

What this does is:

  1. Set a variable called parent - note is uses parent twice and then entity

    You won't see parent or entity in your kint/dpm/dd output, which is a pity because entity is great - load the entity you want to get information from.

  2. Use parent to then get the node id value and title value parent.nid.value and parent.title.value.
  3. Create a link using this variables.

It's quite simple really. You can now use this approach to get other fields/data from your host node.

Sep 13 2019
Sep 13

Wanna know my rules when I am writing templates in PatternLab for Drupal view modes such as teasers, cards, search results, etc? Read on, my friend...

BEM Classes for Streamlined Classes

Classes used in the card view mode should be prefixed with card__ not node__

  • We need to make sure that CSS for cards does not leak out into other components
  • We need to make sure we have as little selector specificity as possible - so we have .card__content instead of .card .node__content in our CSS.

Card Variations

If you want to have slight variations for cards, please add that as an option in the classes array. Examples of this might be for a card for a specific content type, or a card when there is no image present, or a card that has the image on the left/right.

{% set classes = [ 
  "card", content_type ? 
  "card--" ~ content_type, image ? 
  "card--has-image" : "card--no-image", 
  alignment ? "card--" ~ alignment, ] 

This will print:

We can then apply overrides using these variations. For example, if the date field on a event is bold but but on a blog is not, we can have code like so:

.card__date { 
  color: $c-grey--darkest; 

.card--event .card__date { 
  font-weight: $fw--bold; 

Avoid Twig Blocks

Try not to use Twig blocks in view modes - {% block image %} If we use Twig blocks, then in the Drupal template anything can be put in the {% block %} to override it. If we change the order of things in the {% block %} in PL, this will have no effect in Drupal, since the Drupal version will be overriding it.

For example, the Drupal template could have something like this:

{% block image %} 
  {{ content.field_date }} 
{% endblock %} 

Avoid Drupal Templates

Try to avoid having templates based on specific content types for view modes. This is usually necessary for Full view mode, but for Teaser, Card, etc let's try to keep them more generic.

If you need to use a content-type template, that is fine; but it should be the exception, not the rule.

In general, since each content type Card should be similar, then each content type should be able to use the same node--card.html.twig template file.

Avoid {% if not page %}

{% if not page %} Should not be needed in view modes. A Card or Teaser will never be a full page. For the same reason, we can usualy leave this out in the content type full view mode templates, since the full view mode will always be a page.

Do Not Hardcode Heading Levels

Unless you know that the Card view mode is never going to have a h2 item above it, do not set h2 or h3 etc as the heading element.

We do not want to have a HTML structure like this:


Views Card Block Title

Card Title

Card Title

Card Title

Instead, we would like this:


Views Card Block Title

Card Title

Card Title

Card Title

In general, view modes will take a h2, so let's place that as our default.

{% set card_title_element = card_title_element|default('h2) %}

<{{ card_title_element }}>{{ card_tite }}

Then, in our Drupal template, we can set the element depending on whether it has a parent h2 or not; for example, if it is in a 'Related Content' building block and that building block has a title such as:


We can do so in the Drupal template with a variation of this:

{% if node._referringItem.parent.parent.entity.field_p_rc_title.value %} 
  {% set teaser_title_element = 'h3' %} 
{% endif %} 

If the above returns as false, our default h2 from the PL pattern will kick in.

Use Prefixes for Variables

Let's use prefixes for variables, using {{ card_content }} instead of {{ content }}. This will help avoid global variables being used by accident in our components (unless we want them to, such as the image_alt variable from data.json). The {{ content }} if used, will not necessarily print the content of your component when that component is used outside of its own context: for example, if you print a card in a views list in a sample page.

Variables for Specific Content Types

If there's a variable for a specific content type, such as the location field for an event card, we can just wrap that in an {% if %} statement, like so:

{% if content_type == 'event' %}
  {% if event_location %}

    {{ card_location}}

  {% endif %} 
{% endif %} 

Then that variable will not print in any other content type's template.

May 14 2019
May 14

I was playing around with the SpeechRecognition API last night and thought, "wouldn't it be cool if we could use voice to administer a website?". Then, I put together this tiny proof of concept module for use with Drupal.

Here's a short video of it in action.

Ok, that looks pretty cool. Show me the code.

window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; 
const recognition = new SpeechRecognition(); 

recognition.interimResults = true; 
recognition.addEventListener('result', e => { 
  transcript = Array.from(e.results).map(result => result[0]).map(result => result.transcript).join(''); 
  const statement = e.results[0][0].transcript; 
  if (statement === "voice admin new page") { 
    window.location.href = "/node/add/page"; 
  } else if (statement === "voice admin new article") { 
      window.location.href = "/node/add/article"; 
    } else if (statement === "voice admin log out") { 
      window.location.href = "/user/logout"; 
    } else if (statement === "voice admin go home") { 
      window.location.href = "/en"; 

// When we stop talking, start the process again, so it'll record when we start 
// talking again. recognition.addEventListener('end', recognition.start); recognition.start(); 

WTF? That code is crap. You have hard-coded what you want the site do to. That's not going to work for all sites, only for your specific use case.

Yep, that's true at the moment. Like I say, it's just a proof-of-concept. We'd need to create a settings page for users to decide if they want it to be available or not. And we'd have to create a better parsing system that listens for "voice admin" and then starts recording. Then we'd need to make sure that it patches together the fragments of speech after this to construct the action that you want to perform. Following that, it would be really cool if on the settings page users could type in commands and responses that SpeechRecognition will listen for and then perform.

I think all this is very possible, and probably not too much work. It would be great to see more progress on this.

If you'd like to create a pull request, the code is available on GitHub.

Apr 10 2019
Apr 10

We had a case where on some views we needed to print the header above the exposed filters and on others we needed to print the header after the exposed filters. Here's my simple solution.

This is rather simple, in my views template (list.twig I call it, as I am using PatternLab), I created the following:

{% if not header_after_exposed and header %}
  {{ header }}
{% endif %} 

{% if exposed %}
  {{ exposed }}
{% endif %} 

{% if header_after_exposed and header %}
  {{ header }}
{% endif %} 

Now, in my corresponding views template in Drupal - views-view--search.html.twig - I just use that template and set the header_after_exposed variable to true.

 {% set header_after_exposed = true %} {% include '@content/list/list.twig' %} 
Mar 07 2019
Mar 07

Linking patterns allows us to give our users a real feeling for how the website is going to work, on real devices, which things like InVision can never do. Here's some simple approaches.

When using PatternLab, you can link to a pattern by creating a variable such as {{ url }}. Then in your corresponding JSON or YML file, you can setting this variable equal to something like

url: link.pages-contact


url: link.pages-homepage.

We often use this when creating menu items, since in Drupal our menu items template looks for two parts to the menu link: title and url, something like this:

       title: 'About Us' 
       url: link.sample-pages-basic-page 
      title: 'Contact Us' 
      url: link.sample-pages-basic-page-contact-us 

This works great when working with a template that has a specific variable for the URL, such as the link to a node in node.html.twig, so we can link the title in our teaser template in PL to our sample blog pattern, for example.

But if we have a link field, such as a Call to Action in a paragraph bundle we might have something like this in our pattern:

{{ cta_link }}

and this in our corresponding YML file:

cta_link: 'Click Me!' 

We don't have PL paths in those links, because if we swap `#` for a `link.sample-pages-basic-page` it'll just render that as a string. And we don't want to break the variable into two parts, because in the Drupal template, we want to be able to {% set cta_link = content.field_cta %} and let Drupal do all its render magic.

The solution? Don't break up variable into two parts, concatenate what you want in YML instead to allow us to link to specific patterns:

  join(): - 'See Ways to Help'

Now, the first part will render as a string, the second as a variable to the pattern you want to link to, and the third part as a string.

We could also create a link pattern, and do something like this:

    pattern: 'organisms-link' 
      url: 'link.sample-page-homepage' 

I don't, because, in general, I don't like patterns to depend on other patterns. This is simply so I can drag and drop them from project to project without any friction. Each component has everything it needs contained within it. It also means in case of something like a link field, we can let Drupal do as much of the heavy lifting as possible.

Jan 08 2019
Jan 08

Creating an 'Add to Calendar' Widget in Drupal

A simple request: we need an 'Add to Calendar' widget to add our events to Google Calendar, iCal, and Outlook. Simple (once I had completed it!).

There's a module for that. There is, it's called, obviously, addtocalendar. It works very well, if you:

If you don't want to use an external service for something as simple as adding an event to a calendar, then it looks like you'll need a custom solution. Their smallest plan only allows 2 events per month.

The PatternLab Part

Here's the custom solution I came up with (in the future, I'll look at creating a module for this with a settings/UI page for site builders). Note, it's a PatternLab implementation; if you don't use PatternLab and just want to work directly in your Drupal theme, it would be even easier.

Here's the code for the 'Add to Calendar' pattern in PatternLab (some classes and things are removed to make it easier to read):

{% set classes = [ "add-to-calendar" ] %} 

{% set ical_link = 'data:text/calendar;charset=utf8,BEGIN:VCALENDAR%0AVERSION:2.0%0ABEGIN:VEVENT%0ADTSTART:' ~ atc_start_date|date("Ymd\\THi00\\Z") ~ '%0ADTEND:' ~ atc_end_date|date("Ymd\\THi00\\Z") ~ '%0ASUMMARY:' ~ atc_title ~ '%0ADESCRIPTION:' ~ atc_details|striptags ~ '%0ALOCATION:' ~ atc_location|replace({'
': ' ', '
': ' ', '

': ' ', '

': ''}) ~ '%0AEND:VEVENT%0AEND:VCALENDAR' %} {% set google_link = 'https://www.google.com/calendar/r/eventedit?text=' ~ atc_title ~ '&amp;dates=' ~ atc_start_date|date("Ymd\\THi00\\Z") ~ '/' ~ atc_end_date|date("Ymd\\THi00\\Z") ~ '&amp;details=' ~ atc_details|striptags ~ '&amp;location=' ~ atc_location|replace({'
': ' ', '
': ' ', '

': ' ', '

': ''}) %} {{>

What does the above code do?

  • Creates a Google Calendar variable and creates an iCal variable. Outlook will also use iCal.
  • Uses these variables as links to add the event to their respective calendars.

Within the variables, we have some more variables (start date, end date, etc), which we should probably wrap in conditional statements so that their clauses don't print unless they are present in Drupal (some fields might be optional on your event content type, such as end time).

These variables are:

  • atc_start_date: Start Date and time
  • atc_end_date: End Date and time
  • atc_title: the name of the event
  • atc_details: description for the event
  • atc_location: place of event

In our Event pattern in PatternLab, we then have a variable called 'add_to_calendar' so that events have the option to have this widget or not. In event.twig, we simply print:

 {% if add_to_calendar %} {% include '@site-components/add-to-calendar/add-to-calendar.twig' %} {% endif %} 

The Drupal Part

In Drupal we create a boolean field on our event content type field_event_add_to_calendar, if this is ticked, we will display the Add to Calendar widget.

Here's the code from node--event--full.html.twig

{# Set the Add to Calendar Variables #} 

{% if node.field_add_to_calendar.value %} 
  {% set add_to_calendar = true %}
{% endif %} 

{% if node.field_event_date.value %} 
  {% set atc_start_date = node.field_event_date.value %} 
{% endif %} 

{% if node.field_event_date.end_value %} 
  {% set atc_end_date = node.field_event_date.end_value %} 
{% endif %} 

{% if node.title.value %} 
  {% set atc_title = node.title.value %} 
{% endif %} 

{% if node.field_event_intro.value %} 
  {% set atc_details = node.field_event_intro.value %} 
{% endif %} 

{% if node.field_event_location.value %} 
  {% set atc_location = node.field_event_location.value %} 
{% endif %} 


{% include "@content/event/event.twig" %} 

To explain:

If the 'Add to Calendar' boolean is on, we set the add to calendar variable as true. This in turn tells patternlab to render the Add to Calendar component. We then check if each field we might use has a value in it - such as a start date and end date. If so, we map the values from each of those fields to variables in our Add to Calendar component (such as atc_start, atc_title, etc)

Now, when you view a node, you will see your Add to Calendar widget on any nodes that the editors choose to put it. You can see a sample of the Add to Calendar widget in my PatternLab.

Simple, once I figured it out.

Got an improvement for this? The comments are open.

Oct 19 2018
Oct 19

I have a card component with a title, image, text, and link. How come all my card variants are inheriting all the values from the default one? Short answer, you don't. It's a feature, not a bug.

Where this really becomes frustrating is when you have a pattern that lists a number of items in an array. In that case, all variants will have (at least) that many items, even though you may want fewer.

For illustration:

list.twig has something like this:

 {% for list_item in list_items %} 
  {{ list_item }} 
{% endfor %} 

Then list.yml has something like this:

  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
-- loads of more teasers for the main listing page 

Now you want to create a variant of list such as list~related-articles, but with only 2 items. You'd expect this would work

  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 

But, no. This will still render as many items as were in the parent component. That's the beauty (a feature, not a bug) of PatternLab's inheritance system. To stop it you need to do something like this:

  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
    - - - and so on, so each extra one is basically set to 'false' 

When we do this with a component such as a card, we might also want to have variants such as card~no-image, card~no-text, etc. In this case, we'd have a card.yml like so:

​card_title: 'My Card Title' 
card_image: '' 
card_text: 'The text of the card will go here'

However, if we create variants, each of the items in card will be inherited to the variant. You'll notice this if you try to create one super mega component for all variants of a hero component for example (hero title, pre-title, sub-title, image, alignment, cta buttons, etc).

In this case, what I do is create a default component card.yml or hero.yml and give it only values for items that will more than likely be in all variants (basically whatever you are going to mark as a required field in Drupal (or whatever CMS you are using)), then set all others to 'false' in the component. Now when I create variants I only need to override the specifics for that variant, since everything else that is being inherited is already set to false. I also create a 'Kitchen Sink' version of the component which shows every item in action but DO NOT create this as the default/reference component.

My default card.yml might look like this:

card_title: 'My Card Title' 
card_image: false 
card_text: false 

Now my variants can look as simple as:


​card_image: ''

And card~long-title will be just one line:

 card_title: 'This is a long title on a card just to illustrate what happens when it wraps to more than one line' 

And that is why this is a feature, not a bug - it allows us to write variants very simply and quickly. Is there a better way of doing this? I'm not aware of one. If you are, drop it in the comments. Thanks.

Sep 22 2018
Sep 22

Regular Structured Communications

There can be a tendency to presume everyone knows what they are doing and should be allowed to work on their own initiative. This is a tendency I agree with, but it’s also of utmost importance to make sure others on the team also know what each person is working on.

Without regular, structured communications (at least weekly, if not daily, meetings), it’s quite possible for one person to work on a component (say a “tile” component) while another person works on another component (say a “card” component) and both of these components to be the exact same item. Except now we have two versions of the same component, just different names for them.

Without regular, structured communications, each team member is working in a silo, not aware of the whole of the project, and being responsible for only their section. This makes it harder to integrate each section of the site into each other. It will also mean it takes more time for the diligent developer to understand each part of the project if they wish to. Regular meetings save time.

One of the great things about regular, structured communications is that they lend themselves very well to team building. Let your team know you are very proud of how much has been achieved this sprint. Congratulate someone for solving a particular tricky issue. Allow a space for people to vent any issues they might be having.

Fewer Developers, More Focussed

It’s a truism and a paradox, but the more developers we have on a project, the longer the project is going to take to complete. Every new developer takes time to get up to speed with the codebase and slows down the momentum of the other team members. This causes projects to go over-time and over-budget.

This is not to say we should have only one developer per project, but I would suggest keeping teams as small as possible. A small number of really focussed developers will work much faster (having such an intimate knowledge of the codebase, feature set, sprint goals, etc.) than a large team of developers working on many small features. A large team lends itself too easily to people changing someone else’s code not expecting there to be any ramifications, to people “fixing” up another’s code because it doesn’t meet coding standards or has a security issue – work that shouldn’t have to be redone.

Use a Design System/Pattern Library

Pattern library tools have become very popular in the past few years. They allow everyone to see an inventory of the different components the team has available to work with when building web pages.

The tool I have chosen to standardise on is PatternLab. There are many others.

They also allow us to show our clients what the designs will look like on real devices, rather than images of what they might look like in InVision or some other tool like that. For years, yes literally years, I’ve been talking at conferences extolling the virtues of using a ‘Design in the Browser’ approach to front-end development and against the practice of using static design tools (Photoshop/Sketch) to deliver designs to clients. By all means, use these tools to deliver the designs to your team, but let the team code up the designs to deliver to your client.

Clients should be sent URLs, not PDFs.

One Component Per Design Feature

Once we are all agreed that we are going to use a pattern library tool, we can then start coding up the features of the front-end. Often components have variations of themselves. An example could be a “Card” component which has an image at the top, followed by a headline; with a variation which has the headline on top followed by the image. In this case we might create a card component and also a card—reversed component.

Sometimes teams can fall into the trap of thinking that “everything” is a variation of a core component. So, for example, we might have a component which has an image on the left, a headline and some teaser text on the right and/or vice-versa. Surely we can just create a component variation such as card—columns and card—columns-reversed? Yes, we can. But then when we see another component that has the same layout, except the image is a different size, there is no teaser text, we have tags under the headline, etc., we are then going to need to make another variation of card— and yet another variation of card— and yet another variation of card— and so on. Technically this is possible, but in practice you are setting yourself up for a maintenance nightmare.

The reason each of these components might look like variations on a core component is because they designers are following brand guidelines and using the same font, same sizing and rhythm rules, same colour scheme, etc. When you think about it like that, everything should look similar – it’s all supposed to fit together, but that doesn’t mean everything is a variation of a core component.

If we want to create all the variations of display types based off one core component, we are going to have a large core file, with a lot of logic and clauses and “if this then that” statements, and an equally large CSS file to take consideration of all the variations. This makes it harder to read the code, harder for new developers to get on-boarded to the project, and harder to maintain.

My suggestion is to create one component per display type. Based on the examples above, we would have a card component and a columns component. My files in PatternLab might look like this:

– card.twig
– card.js
– card.scss
– card.yml
– card~reversed.yml
– card~
– card~
– card.md

– columns.twig
– columns.js
– columns.scss
– columns.yml
– columns~reversed.yml
– columns~
– columns~
– columns.md

This means that all the functionality and styling for the card component is within the card directory. It’s a small core file, with only variations for the image to be top/bottom or for there to be teaser text/no teaser text – very obvious variations of the card. The core file should be easy to read (one CSS class will create the variations), and it’s specific enough to know exactly what this component does.

We can set up a regression test for each component very easily to catch any curveballs that might arise later. None should arise if we use correct BEM naming conventions so that all styling for .card is prefixed with .card and all styling for .columns is prefixed with .columns, like so:

.card { css for card container }
.card__image { css for the image in the card }
.card__headline { css for the headline in the card}

.columns { css for columns container }
.columns__image { css for the image in the columns }
.columns__headline { css for the headline in the columns}

Focus on Features, Not Breakpoints

The Unix philosophy says “do one thing, do it well”. Taking this as a guiding principle, we should focus on developing one feature and completing it (the “card” component, for example) before moving to the next one. Sometimes I talk with teams and they tell me they focus on the desktop first or on the mobile screen views first and try to get the whole website looking correct at one breakpoint before moving on to the next one. While much of the code might be fine for each breakpoint, going over the code to catch all the issues for other breakpoints is going to take longer than completing each feature and having them signed off.

Remember, signed-off features can have regression tests written against each breakpoint. It also means that if the site is going to go over time, it’s possible to launch with a smaller set of (completed) features, rather than a feature-complete website, the front-end of which does not meet all the brand guidelines.

Automate Code Quality

Following coding standards can take time. You write your code, you check back over it, you fix up the indentation and comments style. And – you still miss something. The simple fix? Using code sniffers and/or linters and/or combing tools to automate this for you.

I have CSSComb set up in all my projects, so every time I save a CSS or SCSS file, it’s automatically formatted to follow the (Drupal) coding standards. I use prettier for my JS files, so again, each time I save my file, it’s automatically formatted to meet our coding standards. I have php_codesniffer installed and set to (Drupal) coding standards, so it notifies me each time I have an indentation wrong or something like that.

Why is this important? This is important when working as part of a team to make sure everyone’s code looks the same. It makes it much easier to read code from other developers when it is formatted the same as your code. It’s even more important when you edit a file that someone else has worked on and then try to commit those changes to git. How many of the changes were changes you made (that actually affect the code) and how many were just the csscomb or prettier auto-formatting your code? You want your git commits to be meaningful, so you only want the commit to announce the changes you have made.

What I do now on any file I need to work on is save it the moment I open it, so if another developer doesn’t have automated code checkers built-in, the file will format itself. Then I commit this save with the git commit message “running csscomb” or “running prettier”. After this, I make my changes and my commits are then meaningful – I can see the actual code I changed. This takes more time that it would if everyone just installed code formatting tools from the outset, and is something I think we should insist on for every project.

Front-end in Tandem with Site Building

The chances are that the designs are going to be sent to the client as Sketch files or PDFs or similar and not as PatternLab URLs (despite my best efforts to encourage this for years). Or maybe another agency won the contract to design the website and our agency won the contract to build/maintain it. If this is the case, by the time they get to the front-end developers, they are signed-off – a fait accompli.

Let’s turn this into a positive. As we are creating our front-end components (one component per design pattern, please!), we should also be building out the CMS functionality that this component will use. So, if we have a listing page showing teasers of events in a card display, then we should build out the fields (in Drupal or whatever CMS we use) that support this, and then create the configuration and templates needed to map 1-to-1 from Drupal to PatternLab.

This allows us to “dog food” our own work very early on. We can create our front-end and backend in tandem and make sure that both work seamlessly. We can ask our clients to sign-off on the front-end and CMS features at the same time; and we can then set up regression testing for the front-end and functional testing (with Behat or something else) for the backend. As the project progresses, we will have a featured-complete, fully-tested website and can move from feature to feature (or new feature to new feature during future phases of work) with less fear of things breaking when we make changes.

Regression Test

Here’s a joke: a css selector walks into a bar; a table in another bar across town falls over.

Writing front-end code can often be like playing Whack-A-Mole. It doesn’t have to be.

Regression testing is basically taking a screenshot of your website that you can use as a “reference” (this is what it is supposed to look like). Then, when you make a change, you can take automated screenshots of the site again to make sure that the only things changed were things you expected to change. The regression testing tool I use is BackstopJS.

An example: you have a header, a main content area, and a footer. You set up your reference shots when these are all signed off by the client. In a future sprint you are asked to change the main content area from 1220px wide to 1440px wide on desktops. You make the change, tell your regression testing tool to run the tests and then you get back a report showing you that the main content area is now wider than the header and the footer – as it should be. You see the header and footer were not affected so you can deploy this new item. Now, think about doing this for every component across your website! The regression testing suite will run all the tests in a matter of minutes (while you can be happily working on another feature – multitasking for the win!), whereas it could take you an hour to check everything and you might miss something.

Like automating code quality, automate regression testing: automate the boring things.

Set Time Limit for Being Blocked

If a developer is blocked on something and can’t figure out how to complete the task, that is time wasted. Developers need to know they can ask for advice and they need to know whom they should ask – probably the gatekeeper (see next section).

My general rule of thumb for this is: if I feel I am blocked and just looking at the screen for 15 minutes, I need to ask for help. If I haven’t an idea what to do to unblock myself and I’ve thought hard about it for 15 minutes, that’s enough of the company’s money spent on it.

Ask for help sooner rather than later so you can be as productive as possible.

Nominate Code Gatekeeper

If you need to have a large team of developers, you need to have a code gatekeeper: someone whose job it is to know what components are developed, what ones are in progress, and what is in the backlog.

The gatekeeper also needs to know who developed what component and in what manner, so any components that are re-usable are re-used and one-off components are kept to a minimum. It is the gatekeeper’s job to ensure that no component is developed more than once, just using different names (“card” and “tile” as we saw above).

When someone is blocked or unsure of something, they ask the gatekeeper for advice and guidance so they can continue working as soon as possible. As well as that, the gatekeeper can also step in and try to figure out a solution (or even a proof-of-concept for a solution) while the developer works on something else. When the gatekeeper has an approach figured out they can tell the developer about it and then the developer can get back to that feature as soon as possible. The gatekeeper is a pair of fresh eyes.

The gatekeeper might be the lead developer on the project or it could be a separate role. The gatekeeper should write as little code as possible for the actual production version of the site, and, rather, should focus on the high-level overview of the project and working on solutions to unblock developers.


That’s it. That’s my thoughts on putting together and running a front-end (or teams) that can effectively work on large, enterprise websites. If you have a small team or are a freelancer, I think what I have said above also holds true.

Any thoughts or comments, leave them below. Thanks.

Sep 20 2018
Sep 20

Responsive images in PatternLab get a bit of a bad rap sometimes, because they are tricky to have in PL and Drupal. Here's my "easy way" of achieving it.

This came up today in the DrupalTwig Slack (join it). A user wanted to know how to use responsive images with the Emulsify Drupal theme. I don't use the Emulsify theme (yet - I will soon), though Four Kitchens, the geniuses who created it, have responsive images built in. Recently I created my own - simple and rudimentary, but it works a treat.

I first create a "Responsive Image" pattern. In this I have two files - responsive-image.twig and responsive-image.yml. Here's the contents:



    - 'https://placeimg.com/500/500/nature 500w, '
    - 'https://placeimg.com/1000/750/nature 1000w, '
    - 'https://placeimg.com/1440/475/nature 1440w'

image_sizes: '(max-width: 600px) 100vw, (max-width: 960px) 100vw'

image_src: ''https://placeimg.com/1440/475/nature'

To use it in another component, I just call a variable and set that variable in the YML file.

For example, to call the hero image as a responsive image in my event component, I'll print this: {{ hero_image }}. Then in my corresponding event.yml file, I'll define the hero_image item like so:

    - include():
        pattern: 'basic-elements-responsive-image'
              - 'https://placeimg.com/600/600/tech 500w, '
              - 'https://placeimg.com/1200/360/nature 1000w'
         image_src: 'https://placeimg.com/1200/360/nature'

The {{ image_src }} variable is needed to provide a default fallback for browsers that don't support srcset or sizes, such as IE9/10/11.

Then in my Drupal template I just swap my image field variable for the responsive image one, like this:

{% if node.field_hero_image.value %}
  {% set hero_image: content.field_hero_image %}
{% endif %}
{% include ... usual path to component stuff ... %}

Drupal then renders the image field using whatever settings I have given it in Drupal - presumably responsive image ones.

This post might not help you if you are using Emulsify, but it might help others who stumble upon it.

Sep 08 2018
Sep 08

Yes, I know, there's more than one way to integrate PatternLab with Drupal. Here's how I create a card component and map it to Drupal.

Here's the task - create a card component with fields for:

  1. Card Image
  2. Card Title
  3. Card Body
  4. Card Link URL
  5. Card Style (sets a colour for a border-top on the card)
  6. Card Size (sets the width of the card)

In PatternLab, here's what our Twig file might look like (with explanations after it):

set classes = [
  card_image ? 'card--has-image',
  card_style ? 'card--' ~ card_style,
  card_size ? 'card--' ~ card_size,

{% if card_link_url %}
  {% set element = 'a' %}
  {% else %}
    {% set element = 'div' %}
{% endif %}

<{{element}}{{ attributes.addClass(classes) }}{% if card_link_url %} href="https://mark.ie/blog/creating-a-card-component-in-patternlab-and-mapping-to-drupal-the-right-way/{{ card_link_url}}"{% endif %}>

  {% if card_image %}

{{ card_image }}

{% endif %} {% if card_title %}

{{ card_title }}

{% endif %} {% if card_text %}

{{ card_text }}

{% endif %} {% block content_variable %} {# This allows the cache_context to bubble up for us, without having to individually list every field in {{ content|without('field_name', 'field_other_field', 'field_etc') }} #} {% set catch_cache = content|render %} {% endblock %}

The classes array at the top allows us to set variations for our card, depending on values chosen by the editor. So, if there is an image, we add a class of .card--has-image; if a style is chosen, we add a class of that style, for example: .card--medium (I create options for small, medium, large, and full - with 'small' being the default - corresponding on large screens to a width within their container of 33%, 50% 66% and 100% respectively).

Next, we set our {{ element }}. This allows us to have the card wrapped in an a tag or a div tag. We check to see if the link field has been filled in and, if so, we use the a element, but if not, we use the div element instead. This will render HTML like one of the following:



Following this, we check if there is an image and, if so, we render our image div. Checking first allows us to have nice bem-style classes, but also means we don't end up rendering emtpy divs. Although, when it comes to Drupal, what's another div!

We then do the same for the title and body.

The funny looking part at the end about cache was inspired by an article about Drupal block cache bubbling by PreviousNext. The specific code came from this Drupal.org issue. The PreviousNext article says to render the {{ content }} variable with our fields set to 'without', because without the {{ content }} variable rendering, caching is not working properly (I don't know enough about caching to explain more). However, on a content type with loads of fields, it's very cumbersome to add every field in with {{ content|without('field_image', 'field_tags', 'field_other', etc) }}. Instead, I put that {{ catch_cache = content|render }} at the bottom of each of my content patterns - node, block, paragraphs, etc, then don't need to add it later in Drupal.

The SCSS for this looks like this:

// Theming individual cards

.card {
  width: 100%;
  margin-bottom: $base-line-height;
  border-top: 0.5rem solid $c-primary;
  background-color: $c-grey--lighter;

a.card {
  text-decoration: none;
  &:hover {
    background-color: $c-primary;
    background-image: linear-gradient($c-primary, darken($c-primary, 15%));

.card--has-image {
  background-color: $c-white;

.card--small {
  @include breakpoint($bp--medium) {
    width: calc(33% - 2rem);
.card--medium {
  @include breakpoint($bp--medium) {
    width: calc(50% - 2rem);
.card--large {
  @include breakpoint($bp--medium) {
    width: calc(66% - 2rem);
.card--full {
  @include breakpoint($bp--medium) {
    width: calc(100% - 2rem);

.card--primary {
  border-top-color: $c-primary;
.card--secondary {
  border-top-color: $c-secondary;
.card--tertiary {
  border-top-color: $c-tertiary;
.card--quaternary {
  border-top-color: $c-quaternary;
.card--quinary {
  border-top-color: $c-quinary;
a.card--primary {
  &:hover {
    background-color: $c-primary;
    background-image: linear-gradient($c-primary, darken($c-primary, 15%));
a.card--secondary {
  &:hover {
    background-color: $c-secondary;
    background-image: linear-gradient($c-secondary, darken($c-secondary, 15%));
    .card__text {
      color: $c-white;
a.card--tertiary {
  &:hover {
    background-color: $c-tertiary;
    background-image: linear-gradient($c-tertiary, darken($c-tertiary, 15%));
    .card__text {
      color: $c-white;
a.card--quaternary {
  &:hover {
    background-color: $c-quaternary;
    background-image: linear-gradient($c-quaternary, darken($c-quaternary, 15%));
    .card__text {
      color: $c-white;
a.card--quinary {
  &:hover {
    background-color: $c-quinary;
    background-image: linear-gradient($c-quinary, darken($c-quinary, 15%));
    .card__text {
      color: $c-white;

.card__content {
  padding: $base-line-height;

.card__title {
  color: $c-grey--darker;
  font-family: $ff--alternate;

.card__image img {
  width: 100%;
  height: auto;

.card__text {
  color: $c-grey--dark;
  p:last-of-type {
    margin-bottom: 0;

We can do the site building very easily with the paragraphs module. Create a paragraph of type card, add the fields

  1. Card Image - media image
  2. Card Title - text (plain)
  3. Card Body - text (long, formatted)
  4. Card Link URL - link
  5. Card Style (sets a colour for a border-top on the card) - text (list)
  6. Card Size (sets the width of the card) - text (list)

Then, in our paragraph--card.html.twig file, we write the following code:

{% if paragraph.field_p_card_style.value %}
  {% set card_style = paragraph.field_p_card_style.value %}
{% endif %}

{% if paragraph.field_p_card_size.value %}
  {% set card_size = paragraph.field_p_card_size.value %}
{% endif %}

{% if paragraph.field_p_card_link.value %}
  {% set card_link_url = content.field_p_card_link.0['#url'] %}
{% endif %}

{% if paragraph.field_p_card_image.value %}
  {% set card_image = content.field_p_card_image %}
{% endif %}

{% if paragraph.field_p_card_title.value %}
  {% set card_title = content.field_p_card_title %}
{% endif %}

{% if paragraph.field_p_card_text.value %}
  {% set card_text = content.field_p_card_text %}
{% endif %}

{% include "@building-blocks/card-layout/_card.twig" %}

What the above does is checks if the card paragraph has values in its fields and then sets variables if it does. This means we don't render empty divs.

You will also notice that I render each field's full content for image, title, and body. This is to keep all the Drupal goodness we have in the attributes object - for accessibility and to make sure things like contextual links/quick edit still work.

You will often see the same template written like this:

{% include "@building-blocks/card-layout/_card.twig"
  with {
    card_style = paragraph.field_p_card_style.value,
    card_size = paragraph.field_p_card_size.value,
    card_link_url = content.field_p_card_link.0['#url'],
    card_image = content.field_p_card_image,
    card_title = content.field_p_card_title,
    card_text = content.field_p_card_text

I find doing that leads to fields such as {{ content.field_image }} always returning true because of Drupal's rendering system. So, even if we don't have an image, we'll still have an image div, whereas doing an explicit check before we {% include %} our variable seems much safer.

That's it - PatternLab + Drupal integrated beautifully (I think) the "right" way (according to me - you might differ).


You can see a sample of this in action on this site's PatternLab.
Note - I call cards in my PatternLab 'tiles' and make them part of a wrapper component called 'Tiled Layout', which allows me lots of flexibility for cool layouts with little effort.

Jul 28 2018
Jul 28

You know the scenario - you want to list nodes that have the same taxonomy term(s) as the node you are currently viewing. Easy, but you also want to exclude the currently-being-viewed node from the list. Always trips me up.

Each time I have to do this, I read a blog or two or a Drupal issue or two and still I always end up with a quirk. Here's what I normally do:

  1. Create the view
  2. Add a contextual filter for the taxonomy field you want to filter by
  3.  Provide default value
  4. Taxonomy term ID from URL
  5. Load default filter from node page, that's good for related taxonomy blocks
  6. Limit terms by vocabulary
  7. Click Apply

Now I'm Stuck

This gives you a list of nodes related to the current one, but the current node will always show up in your list. If you edit that contextual filter and expand the 'More' tab at the end, and then choose 'Exclude: If selected, the numbers entered for the filter will be excluded rather than limiting the view.' you will be forgiven for thinking this will exclude the current node. IT WON'T. In this case, it will exclude the currently selected taxonomy term - which is the opposite of what you want to do.

The Solution? Another Contextual Filter

  1. Create another contextual filter for 'ID', as in, the Node ID.
  2. Provide default value
  3. Content ID from URL
  4. Scroll to bottom of page and expand the 'More' tab
  5. Click Exclude: If selected, the numbers entered for the filter will be excluded rather than limiting the view.

Now, the second filter will exclude the currently-being-viewed node, while the first filter will do the related-node-taxonomy-magic-dance.

Jul 17 2018
Jul 17

To the future or to the past, to a time when thought is free, to the next time when I need to get the value of file field to use as a variable in Drupal 8 with Twig.

Working my way down through one of Drupal's render arrays of doom to try to get the URI of a file in a media field (in a paragraph type), I came up with this. If you can improve it, feel free to drop a note in the comments:

{% set slide_url = file_url(content.field_p_ei_speaker_slides[0]['#media'].field_m_file_file.entity.uri.value) %}

In steps:

  1. Get the {{ content }} variable
  2. Drill down into the media field (Speaker Slides - pdf, ppt, etc)
  3. Get the first element (0 - it's not a multi-value field in this case)
  4. Load up the #media object
  5. Interrogate the field on the media entity that has the file attached (the File field)
  6. Load this entity (entity here is not presented as an item in the {{ dpm() }} but it's very handy to know
  7. Get the uri.value from here
  8. Wrap it all in a file_url() function

For clarity, here's what I had in PatternLab:

  {# Begin Slides Download #}
  {% if event_slide_download %}
  {% endif %}
  {# End Slides Download #}

And here's what I have in the corresponding Drupal paragraph.html.twig tempate:

{% if paragraph.field_p_ei_speaker_slides.value %}
  {% set event_slide_download = true %}
  {% set slide_url = file_url(content.field_p_ei_speaker_slides[0]['#media'].field_m_file_file.entity.uri.value) %}
  {% set event_slide_download_link = slide_url %}
{% endif %}

{% include "@building-blocks/event-section/event-item.twig" %}

So now, my future self, you will know where to find this next time.

For posterity, here's a blog by Norman Kamper on how to create a custom field formatter, written as a response to this post, and the code is available on github. Thanks Norman.

Mark, I finally found the time to write things down. Published it on Medium as I don't find the time to relaunch my own site to publish it there (a common web developer's disease I guess ?): https://t.co/pXdS7OtYe1

— Norman Kämper-Leymann (@leymannx) July 30, 2018
Jun 21 2018
Jun 21

Here's a very short video demo of editing a menu using Drupal's Settings Tray module. Things like this will be what drives Drupal adoption.

I'm a big fan of the quick edit module for Drupal. If it could work better with paragraphs module, it'd be a knockout feature. Aligned with that, I'm really impressed with the settings tray module and can see so many uses for it in the future - sidemenus, shopping cart slideouts, node editing, etc. Here's a very short video of using it to edit a menu, which should make many content editors' lives easier.

Jun 18 2018
Jun 18

Let's revisit my recent post and see if we can come up with more user-friendly names for PatternLab items.

My Approach to PatternLab recently got quite an amount of discussion on Slack and other places about PatternLab and naming conventions, especially the line "Clients do not want a science lesson". In that I set out my current naming convention like so:

  • Basic Elements
  • Site Blocks
  • Building Blocks
  • Content
  • Sample Pages

While generally appreciated, some people criticised it for being too Drupal-centred. What happens if your client doesn't want to use Drupal? What happens if you want to use the same PatternLab instance for an app on Android or iOS? Good questions, and they got me thinking more. A number of people on Slack recently have been asking about what naming conventions besides the atoms > molecules > organisms one people have been using.

I had a verrrrry long chat (over 3 hours) with some developers from outside of my work place to see what what naming convention(s) might make sense, be easy for clients to understand, and allow enough scale to be used outside of Drupal. Here's what we came up with:

  • Utilities
    • Items such as utility classes like .visually-hidden or .padding-top
  • Base
    • Items such as colours and fonts
  • Elements
    • Low level elements such as headings, paragraphs, basic lists
  • Components
    • High definition components such as a teaser view mode, an embedded video component, a list of teasers
  • Layouts
    • General layout classes for the different page designs - with sidebar, without sidebar, etc
  • Mock-ups
    • Rendered 'pages' or other UI interfaces
    • We shied away from 'Pages' here because not everything might be a page, such as a login screen on an iPhone app

I'm quite happy with those naming conventions and think I might start porting some of them to my work at Annertech. (Oh, and by the way, if you want to get really good Drupal developers to work on your website, we're available for hire - contact us!)

May 18 2018
May 18

I'm sometimes asked for an overview of my general approach to PatternLab. Simple: put everything for each component in the same directory!

When working with PatternLab, which I use for all my Drupal themes, including the theme for this website, I don’t use the full atomic approach. I don't use the approach of atoms > molecules > organisms > etc. I’m sure many people seriously disagree with me for that ( I do think it's a very clever concept). Instead I’ve renamed things to match the language we use with our clients.

I tried talking about atoms and molecules to some clients and their eyes glazed over. Clients do not want a science lesson. They do not want to be told that we are going to take two of these atoms, and mix them with one of these atom, and eventually we'll have water. No, they want to know what their final website is going to look like. When I changed the conversation and started talking about ‘Building Blocks’ (what we call our Drupal paragraph types), site blocks (Drupal's search block, branding block), display types (Drupal's view modes such as teaser, search result), etc, they immediately understood. Then we started hearing things like, "Oh, so we can create a page by adding a number of different building blocks?" and "I see, so the search results page is made up of a group of pages using the 'Search Result' display type?". And my response, "Yes!". You see, we are using plain English to ease with understanding.

Another aspect of my approach that I really like is that _everything_ for each of my components is within the same directory. For example, if it’s a nested paragraph component such as an accordion (so we need a paragraph type called 'Accordion' and one called 'Accordion Item') each template and css and js and readme and json and yaml is all in the same folder. That means when I want to reuse one in another project, I don’t need to remember what sub-particles (atoms/molecules) are used to create the organism. It also means my CSS is scoped to that specific component and doesn’t bleed out of it, so making changes or adding new features is very easy, you just scope the new component's CSS to it, so it won't affect other previously-created components.

Now the top bar of my PatternLab that used to say Atoms | Molecules | Organisms, etc has tabs for:

  • Base
    • Colours
    • Spacing
    • Breakpoints
  • Basic Elements
    • Headings
    • Paragraphs
    • Lists
  • Site Blocks (Drupal Blocks)
    • Search Block
    • Login Block
    • Branding Block
  • Building Blocks (Paragraph Types)
    • Accordion
    • Image with Text
    • Video
  • Content
    • Display Types (View Modes)
      • Teaser
      • Card
      • Search Result
    • Lists (Views)
      • Blog
      • Search Results
    • Content Types
      • Basic Page
      • Blog
      • Event
  • Page Sections (Regions)
    • Header
    • Footer
    • Sidebar
  • Sample Pages
    • Homepage
    • Blog Listing Page
    • Blog Node

After that, I have Backstop.js set up to regression test all of these, so each time I create a new component I can quickly run the visual regression tests and check that nothing has broken. Since all my CSS/JS is scoped to each individual component, it's rare if something is.

Apr 25 2018
Apr 25

Mission: you have 2 fields in a Drupal paragraph bundle, one a node reference field and one a boolean field. Show certain fields in the referenced node depending on the value of the boolean field.

That's a question that popped up today in the DrupalTwig Slack. Here's my response, which I implemented a version of recently.  (In that particular case, we had an 'Event' content type with fields for 'address', 'phone number', etc and also a reference field for 'Amenity'. If the fields were filled in in the event content type, they were to be presented, but if they were left blank on the event content type, we had to pull in the corresponding fields for address, phone number, etc from the referenced amenity.) Anyway, my response:

{# Check the value of the boolean field #}
{% if paragraph.field_boolean.value === 'on' %}
  {# Just render the title of the referenced node #}
  {{ paragraph.field_reference.0.entity.label }}
{% else %}
  {# Render the title and the image field #}
  {{ paragraph.field_reference.0.entity.label }}
  {{ paragraph.field_reference.0.entity.field_image.url }}
{% endif %}

{# Ensure that the cache contexts can bubble up by rendering the {{ content }} variable #}
{{ content|without('field_boolean', 'field_reference') }}

Just for clarity - variables in that code snippet are simply made up off the top of my head (this is what happens when answering questions on Slack). I'm sure I have things slightly wrong and you'll need to play with them to get them to work correctly.

Also, the reason for the cache contexts bit? Say thanks to Lee Rowlands from Previous Next for his blog post Ensuring Drupal 8 Block Cache Tags bubble up to the Page

Apr 16 2018
Apr 16

Ever gotten this error: User error: “attributes” is an invalid render array key? Here's what I do to get around it. If you've a better solution, let me know.

When building PatternLab-based Drupal themes, I try to get the Twig in PatternLab to match what I expect from Drupal. So, if I know Drupal has a line like this in its node.html.twig:

I want to be able to put the same thing into my PatternLab template - even though I am not going to use the {{ attributes }} in PatternLab. This means then I can simply let the Drupal template extend from the PatternLab one and not need to worry about anything.

However, when you do this, you will often get an error to say "attributes” is an invalid render array key. How do I get that error message to go away? Simple - I just add attributes to my Pattern's .yml file, like so:


Note: to use this, you need to have the plugin-data-transform by @aleksip (thanks to Aleksip for pointing this out to me on Slack). This can be added to your composer.json require-dev section:

"aleksip/plugin-data-transform": "^1.0.0",

The data.json File

You can do this for each individual pattern, but then you might get an error somewhere else talking about "title_attributes” is an invalid render array key. To get around all these errors, I simply add these items globally to the default data.json file, like so:

  "attributes": {
    "Attribute()": {
      "class": []
  "content_attributes": {
    "Attribute()": {
      "class": []
  "title_attributes": {
    "Attribute()": {
      "class": []
  "rows": {
    "Attribute()": {
      "class": []
  "teaser": {
    "Attribute()": {
      "class": []

The PatternLab Teaser Twig File

Taking the teaser view mode as an example, here's what my PatternLab twig file looks like:

set classes = [
  'node--type-' ~ node.bundle|clean_class,
  node.isPromoted ? 'node--promoted',
  node.isSticky ? 'node--sticky',
  not node.isPublished ? 'node--unpublished',
  view_mode ? 'node--view-mode-' ~ view_mode|clean_class,

  {% if display_submitted %}

Published: {{ node.created.value|date("D d M Y") }}

{% endif %} {{ title_prefix }}

{{ label }} </h2> {{ title_suffix }} {{ content.field_intro }}

Jan 19 2018
Jan 19

After two years of planning, discussing, and (eventually) coding, the "Out of the Box" initiative has just been committed to Drupal Core.

One of the things most often requested of Drupal has been a better experience "Out of the Box", so that when a new users installs it they see something more positive than a message saying "You have not created any frontpage content yet".

To that end, a strategic initiative called the "Out of the Box Initiative" was set up. I was a member of that team. What we sought to do over the past two years was create a website for a (fictional) publishing company. We decided upon the name "Umami" for a food publishing company, publishing articles and recipes. We went through the full web design process - user stories, validation, requirements gathering, wireframes, design, development ... up to creating what we called the "MEGA Patch". And then submitted about 50 versions of it.

This week we hoped our work would be committed to Drupal 8.5.0-alpha1, but we just missed that deadline. Instead, we had a meeting with the product owners last night to have the final "Needs Product Owners Review" tag removed from the "Create experimental installation profile" issue. Here's the video of that demonstration and meeting:

Following that meeting, the tag was removed and our code was committed to Drupal 8.6.x. This means you'll see it shipping in Drupal in September at the latest, but we hope to get the final beta blockers fixed to have it backported to 8.5.0-beta. If you'd like to help squash some of the bugs, follow these "Out of the Box" issues. Here's the tweet from @webchick (THANKS!) announcing it.

So, what is in this commit?

This commit brings a new installation profile to Drupal. The profile is called "Umami" and has a corresponding "Umami" theme. It creates three content types - basic page, article, and recipe. It has listing pages for articles and recipes, some promotional blocks, a homepage, contact form, and search page. It is a fully-featured (small) website built using only (stable) Drupal core modules.

We are not using experimental modules such as content moderation, layout builder, media, etc. Once they are stable, we hope to bring them into the "Out of the Box" experience as well.

If you'd like to install it, try this link on SimplyTest.me.

Dec 30 2017
Dec 30

Integrating a Drupal Text with Image Paragraph Bundle with Patternlab

Let's get to grips with having a text with image paragraph bundle set up with PatternLab, including having options for left/right for the image/text.

PatternLab Image with Text Component

It's a fairly common design pattern for clients to request - upload an image, place text beside it, and choose whether we have the image on the left with the text on the right or vice versa. You can see my PatternLab version of it here.

Okay, first off, in my twig file, I have the following:

set classes = [



{{ content.field_p_it_image }}

{{ content.field_p_it_text }}


The only thing that is anyway special here is the paragraph.* variables. I have named them like so because this is what Drupal is going to give me back (since the machine name of those fields is p_it_alignment (I namespace all my entity fields with the first letter of the entity type - in this case the name stands for Paragraph Image (with) Text Alignment). This then allows me to have options in PatternLab for alignment and background style (light/dark). To achieve this, I have the following in my pattern's .yml file:

    value: left
    value: light

And in my image-with-text~right.yml file, I just need to override those variables like so:

    value: right

Following that, I have variables named content.field_p_it_image and content.field_p_it_text. Again, these are deliberately named like so, because this is what Drupal will give us back after I create a field with those machine names. Again and again, I try to keep my pattern variables in PatternLab the same as what I will get back from Drupal so when I come to adding the Drupal template, it's just one line of code to say "Hi Drupal template, you'll find the code for this file over here!". So, you can decide in PatternLab what the machine name for the Drupal fields is going to be and then get your site-builders to create fields with matching names, or you can ask your site-builders what machine names are being used in Drupal and then use those in PatternLab.

In my pattern's .yml file, I then set those variables like this:

  field_p_it_text: '

A Short Heading

Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

' field_p_it_image: ''

Finally, in our paragraph--image-with-text.html.twig file we have just one line of code:

{% extends "@building-blocks/image-with-text/image-with-text.twig" %}

You can probably guess what the sass looks like:

.image-with-text {
    display: flex;
    &.left {
      flex-direction: row;
    &.right {
      flex-direction: row-reverse;

The images with text above is an example of this pattern in use on a Drupal website.

Dec 21 2017
Dec 21

Integrating a Simple Drupal Text Paragraph Bundle with Patternlab

This is the first post in a series about how to integrate Drupal with PatternLab. In this first blog post, we'll look at a simple text paragraph bundle, which just has one field: text (formatted, long).

PatternLab Text Building Block Example

I see a lot of blog posts and talks around about the benefits of using component-based design, about how we should use design in the browser principles to present designs to our clients, about how styleguides are the best way to have sustainable frontends. I've even written some and given many presentations about it myself. What I don't see a lot of is blog posts about how to actually do it.

So, here's how to (or at least how I) integrate my PatternLab theme (it's based on the Phase 2 PatternLab Drupal theme) with a simple paragraph type.


Create a pattern - you can put it wherever your setup says it should go. Paragraph bundles are probably molecules, but I'm not sure how you set up yours. In my case, I have hacked PatternLab and created a folder called "Building Blocks" - this is where all my paragraph bundles go (and then I also have a "Building Blocks" field in each content type - more about my set up in another blog post.

Call the pattern something meaningful - in this case, I call mine "Text". Next, we write the Twig for the text pattern. This can be as simple as this:

set classes = [

    {{ content }}

Then in my corresponding text.yml or text.json file, I put in some sample content, like so (I like yml):

content: >

This is a Level 2 Heading

This is a paragraph of text followed by a list. Sed posuere consectetur est at lobortis. This is strong while this is emphasised Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Curabitur blandit tempus porttitor. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper.

  • A text item in a list
  • Another text item
    • A sub-item
    • Another sub-item
  • A third item in a list

This is a Level 3 Heading

Followed by some more text. This is a link sed posuere consectetur est at lobortis. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Curabitur blandit tempus porttitor. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Vestibulum id ligula porta felis euismod semper.


Finally, in my Drupal paragraph--text.html.twig file, I extend the above PatternLab file, like so:

{% extends "@building-blocks/text/text.twig" %}

Yes, there is only one line of code in that file.

Some Explanation

Why do I call my variable {{ content }}? Simple, I know that the default variable in Drupal's paragraph module to print the contents of the paragraph is {{ content }}, so if I give my pattern in PatternLab the same variable name, I won't have to worry about matching any variables. I do not need to do something like this:

{% include '@building-blocks/text/text.twig' with {
  content: text

This will become much clearer when we work with more complex paragraph types in later blog posts.

You can see an example of this pattern in PatternLab here, and the text you are currently reading is an example of it in use in a Drupal template. Simple, eh?

Oct 14 2017
Oct 14

Metatag cannot directly extract an image url from a media field referenced by another entity.

I upgraded my site from Drupal 7 to Drupal 8 this week (yes, that's why it's running on Bartik - a PatternLab developed theme will be installed in time).

This morning I enabled the Metagtag module and set some defaults for page title, description, image, etc. The help notes on the image metatag field says "An image associated with this page, for use as a thumbnail in social networks and other services. This will be able to extract the URL from an image field." This is true, except in my case, all the image fields on the site use the Media Entity module, so they are entity reference fields rather than image fields.

When I put in a token of [node:field_main_image], the result in the outputted metatags was:

In that case, "Mark Conroy | DrupalCon Dublin 2017" is the name of the referenced media. I needed to output the image field of the referenced media.

After a little trial and error, I came up with this:


which outputs:

In this case, "field_main_image" is the name of the image field on my content type, and "field_m_image_image" is the name of the image field on my image media bundle.

If you wish to output a specific image style, you can append :default:url to the token like so:




To use the image media item's alt tag for your Twitter alt tag, you can do this:


I hope that helps!

Oct 11 2017
Oct 11

Seems like just yesterday since we held DrupalCon in Dublin, now we're back with our annual Drupal Camp Dublin.

This year's Drupal Camp Dublin has a great line up of speakers from Ireland and abroad, covering such topics as:

  • Building multi-lingual, multi-region websites (Stella Power)
  • Working as a developer with attention-deficit disorder - add (Levi Govaerts)
  • Planning for disruptions (Jochen Lillich)
  • Migrating from Drupal 4 to 5 to 6 to 7 to 8 (Alan Burke)
  • Automating deployments (Luis Rodriguez)
  • Working webform and commerce and paragraphs and display suites and more (Chandeep Khosa)
  • Live debugging a site that's giving issues (Anthony Lindsay)
  • Stop estimating, start forecasting (Mike King)
  • Deploy with Fabric, and test driven development (Oliver Davies)
  • Design in the Browser (yours truly, me, Mark Conroy)
  • Teaching web development at third level (Ruairi O'Reilly)
  • The QA process (Daniel Shaw)
  • Getting started with Docker (Ed Crompton)
  • The new theme coming to Drupal core (Mark Conroy)

And then there's some socials, and our Drupal Ireland AGM, and at least one other talk not announced yet, and ... you get the idea.

The full schedule is available on our website. There are some tickets left (only €20), get them before they are all gone.

Jul 29 2017
Jul 29

You know you want a t-shirt at DrupalCon Vienna, right? I do. read on...

At this year's DrupalCon in Vienna, the Drupal Association will only be providing 'free' t-shirts to people who purchase an early bird ticket (not exactly 'free' when the ticket costs almost €500) - a position I don't agree with (if you can't afford to give a t-shirt in a welcome pack when charging €500 a ticket, there seems to be something drastically wrong with your business model).

After a tweet last night of me sporting a Druid.fi t-shirt that I received at DrupalCon Dublin, I got thinking: how can we do something to make sure everyone gets a free t-shirt? Then I thought, 'why not provide our own'? Heaven knows we all have loads of them.

Today I'm sporting some haute couture from @druidfi pic.twitter.com/hZm4YYDZ4g

— Mark Conroy (@markconroy) July 28, 2017

So, here's the plan. Everyone brings a Drupal-related t-shirt to DrupalCon. It could be one from your company, one from a previous DrupalCon (I've never been to an non-European one so would like one from somewhere else in the world), one from your local DrupalCamp, a DevDays t-shirt, etc. Then, after the Driesnote, while we are all getting into position for the group photo you give that t-shirt to someone of a similar size to you, and they give you they one they brought (if they brought one). Feel free to bring more than one t-shirt to cover those who don't bring any (it might be some people's first Drupal event).

Oh, and by the way, my size is 'medium'!

Apr 07 2016
Apr 07

Mossack Fonseca's 'client portal' (the part where all the information the world is interested in was uploaded) is running on a version of Drupal (7.23) that they haven't updated in over 3 years. We're now on version 7.43.

There was a MASSIVE deal when 7.32 came out as it patched a very serious security hole. If you're site wasn't patched within 7 hours, you could consider it hacked. We had all ours patched within 45 minutes.

Not keeping your website's security update patches applied will get you in trouble.

They still haven't updated their website, so if you wanted to hack them again, you could simply use one of these scripts: https://github.com/…/drupalg…/blob/master/attack/exploit.php

(P.S. I am NOT suggesting anyone should hack them - just illustrating how easy it is - be careful when you trust your sensitive information to others.)

If you're anyway interested, the theme to power their portal cost a whopping $48.

And it seems their server is running on PHP 5.2, which reached end of life over 5 years ago.

Sep 14 2015
Sep 14

As the title says, I am announcing the launching (soon) of the Drupal Showcase Podcast. This will be a forthnightly podcast showcasing the best new Drupal websites.

Each show will be a friendly conversation (lasting approximately 30 minutes) giving participants a chance to share their thoughts on a recent project, how it was built, what others can learn from it, and contributions made to the Drupal community from the project.

The site is complete in terms of functionality, some more theming is needed, and some more interviews need to be added to the backlog. I'm hoping to launch within about 6 weeks.

In the mean time, join the mailing list for updates and/or suggest a website for the podcast, or chat with me at DrupalCon Barcelona about participating in a show.

Thanks, and keep Drupalling!

(Oh, and the website is here: http://dscpc.mark.ie)

Jun 23 2015
Jun 23

Sometimes, when testing an update hook to a Drupal module, you may need to revert it.

For example, you are setting some permissions for a certain role, you run the update but then realise there was another permission you should have enabled. Instead of adding another update hook, you can revert the update you just ran, amend your update hook, and then re-run the update.

Note*: this is only for reversible changes - like adding/removing permisisons; it will fail if the update had created a new field for example.

It's as simple as this SQL query:
update system set schema_version = XXXX-1 where name = 'module_name';

This means, you are telling your database to update the table 'system' using scheme_version 'XXXX-1' (this will be something like 7001 or 7137 or whatever N equals in your hook_update_N() function minus 1, so if the update was hook_update_7138 you would use 7137 where XXXX-1 is), and apply this update to 'machine name of the module'.

Note*: Do not try this unless you know what you are doing. Make sure you have a backup of your database in case things go wrong - of course you do, don't you?

*Note - notes were added from items left in the comments, to bring them to the body.

Nov 06 2014
Nov 06

I love Karl Marx's maxim, from each according to his abilities, to each according to his needs.

I try to live my life along the lines of open source philosophy. Actually, I try to live my life following the tenets of anarchism - no hierarchies, a system of relations towards others built on trust, friendship, helping each other, solidarity, and distributed leadership (if leadership is the right word here).

When I came upon open source software (specifically Drupal), I quickly realised that anarchism and open source software have so much in common. Drupal is distributed, it is without formal leaders (except Dries as project lead - I know, this is a very simplistic reading of Drupal's set up). Developers work in solidarity/cooperation with each other, sharing code and knowledge to build the best product we can. We are friends, we trust each other.

It was inevitable, then, that I would think an organisation such as CoderDojo was such a good idea. Many, many groups around the world. Everyone helping each other to learn. All sharing knowledge. However, living in a small town in rural Ireland, I wasn't sure it would work here - we don't have coders, we don't have a meeting room, we don't ... Hang on. Let's not be negative, let's give it a try.

So tonight, we have our inaugural meeting of Portumna CoderDojo in the local adult education centre. With use of the computer room. And me as mentor. And some more parents as supervisors. And about 20 kids ready to start coding.

This looks workable. And exciting. Wish us luck.

Oct 11 2014
Oct 11

I'm lazy. So I create shortcuts. You should be like me!

When site building in Drupal, I am constantly using the drush si (site install) command to rebuild the site and check that my new feature is working correctly. Whilst this is a great way to get the site re-installed in super-quick time, it has a built in security feature of creating a user called admin (fine by me) and a new password for this user (which I then change to something simple like "admin" - just for development purposes). This is quite simple:

drush user-password admin --password="admin"

When I'm given a new project to work on, the first thing I do with my local instance is set the admin password to "admin".

To speed things up, I decided to write a drush alias to make these two use-cases easier to manage (yes, I know, first world problems!). And then I thought, why not share this and some of my other aliases. So, here goes (with comments):

alias dr='drush' // general alias for drush
alias drcc='drush cc all' // clear all the caches
alias drup='drush up' // update core and contrib modules
alias drupdb='drush updb' // run update.php
alias dren='drush en' // enable a module/theme
alias drdis='drush dis' // disable a module/theme
alias drfl='drush fl' // list all features on the site
alias drflo='drush fl | grep Overridden' // list all overridden features
alias drfu='drush -y features-update' // update a feature - you must put the feature's name after this
alias drfua='drush -y features-update all' // update all feature
alias drfr='drush -y features-revert' // revert a feature - you must put the feature's name after this
alias drfra='drush -y features-revert all' // revert all features
alias drpad='drush user-password admin --password="admin"' // change password for user admin to admin

Note: to get these working, I put them in my bash_profile file. Every time I add a new one, I run source ~/.bash_profile to reload the file and make the alias available to drush.

What aliases do you use?

Jul 23 2014
Jul 23

It's not difficult. It really isn't, but people struggle over it. Lots. Let's see how to make an image gallery in Drupal 7 (the same theory will hold for Drupal 6 and, I presume, Drupal 8).

This will create an "Image Gallery" content type, with images within each gallery re-orderable by 'drag and drop'. Here we go.

You will need:


  • First off, enable the modules listed above.
  • Then we need to create a content type. In this case, we'll call it "Image Gallery".
  • Next, we need to create an image field inside this content type.
    • Choose "Image" as the field type and "Multiupload" as the widget type
    • Set the number of allowed values for this field to "Unlimited".
  • Create some image presets, such as:
    • Gallery Thumbnail (150px x 150px) - used on the gallery page to list the images in that gallery.
    • Gallery Full Size (1000px wide) - used in an overlay when Gallery Thumbnail images are clicked on.
  • Go to your "Manage Display" page for this content type and for "Full Content" set the following displays for the image field
    • Label = Hidden
    • Image = Colorbox
      • On the colorbox settings, set "Content Image Style" as "Gallery Thumbnail" and "Colorbox Image Style" as the "Gallery Full Size"
      • There are some other options here as well. Feel free to experiment.
      • Click "Update".
    • Click "Save".

Now you are ready to create an image gallery. You may need some CSS to float the images left/right and set some margins/padding.

You can then use Views to list the galleries and link each item in the list to its corresponding gallery.

(Note: a version of this is available on Drupal.org.)


About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web