By now you may have noticed that building websites using components is the latest trend in web development. There are lots of resources out there that explain what components are and how they work. If you need more info on this you can take a look at Component-based theming in D8 by Chris Doherty, or my own talk on Component-driven drupal theming.  

In short, Components are a new approach to top-to-bottom theming focused on breaking down your website in pieces and individually theming those pieces. This presents several advantages including reusability of components, better and semantic markup, as well as modular css styles. If you watch my talk above you will see that my personal favorite benefit is having full control of the markup, which makes all the difference in the theming process. In a D8 project this means we can use the Stable base theme to eliminate as much of the default markup Drupal produces out of the box.  In a Drupal 7 project you could use the Fences module to do the same on a per field basis.

I thought I would write a blog post in which I detail the steps for building components and integrating them into Drupal 8. However, this was a pretty involved process and a single blog post would had made it the longest post in history.  Instead, I decided to break it down into smaller chunks which will be easier to follow along.

This series will be broken down as follows:

  1. Building components with Twig

  2. Prepping Drupal for Component Integration

  3. Putting it all together

Meeting our Component

 Let’s meet the component we will be building in this series. The name of the component is Card and here are the various elements that make it up as well as how the end product will look:

  • Photo

  • Name

  • Label (Follow on)

  • Social Media icons

  • Biography

The idea is to be able to place this component on any page in our site and have it look and behave the same. The View full biography link will toggle a drawer/accordion behavior allowing us to open/close the drawer to read the full biography of the person.

Example of Card component

Building the Component with Twig:

There are several ways or tools for building components intended for Drupal. Two of the most popular ones I know of are Pattern Lab and KSS Node. At Mediacurrent we use KSS Node but this blog series is tool agnostic so you can apply it to your existing workflow.

The way we build components is grouping all the pieces that make up a component in folders.  This includes markup (HTML), Sass, JavaScript and assets (if any). Our card component structure would look like this: 

/card
  /assets
  card.js
  card.json
  card.scss
  card.twig
  • /card = The folder that contains everything about our component.

  • /assets = Folder where we place images, icons, etc. related to this one component.

  • card.js = JavaScript needed for component’s behavior (open/close in this example).

  • card.json = Provides object which defines all variables and stock content for component.

  • card.twig = Consumes content of card.json wrapped into component’s markup.

The above describes how most components will be organized. Some may or may not need Javascript or assets, but just about all of them will need styles, markup and some stock/dummy content in .json format.

It’s important to note that the component described above is only intended for the living styleguide that KSS Node provides. At this point, Drupal is unaware this component even exists.

Next I will break down the code used in each of the files above and explain how everything ties in together.

card.twig (github gist):

<div class="card">
  <div class="card__image">{{ image }}</div>
  <h2 class="card__name">{{ name }}</h2>
  <div class="card__social-media">
    <div class="card__social-media--label">Follow on:</div>
    <ul class="menu card__menu--social">
      {% for item in socialLinks %}
        <li class="card__menu--social--item">
          <a href="{{ item.url }}" class="icon-{{ item.icon|lower|clean_class }}">
            {{ icons.get(item.icon) }}<span>{{ item.icon }}</span>
          </a>
        </li>
      {% endfor %}
    </ul>
  </div>
  <button class="card__view-bio">{{ showBio }}</button>
    <div class="card__bio">{{ bio }}</div>
</div>

The markup above is not only clean and easy to understand, but it is also semantic, which means it has meaning and is descriptive of the information it represents. This is good for Search Engine Optimization (SEO), as well as accessibility.

Any items you see as {{ something }} is Twig specific code extracted from the .json file.

JSON (Github gist):

{
  "image": "<img src='assets/miriam-werner.png' alt='Miriam B. Werner'/>",
  "name":"Miriam B. Werner",
  "bio":"Miriam B. Weiner is an experienced travel writer and editor based in Washington, D.C. She has traveled all around Europe, as well as to countries in Africa and the Middle East. Now she’s set her sights on visiting the U.S. National Parks while providing the inspiration and information others need to make the most of America’s wilderness.",
  "showBio": "View full biography",
  "hideBio": "Hide biography",
  "socialLinks": [
    {
      "icon": "instagram",
      "url": "http://instagram.com"
    },
    {
      "icon": "twitter",
      "url": "http://twitter.com"
    },
    {
      "icon": "youtube",
      "url": "http://youtube.com"
    }
  ]
}
 

The .JSON file defines an object where we have a collection of variables and values which we are passing to our twig file.  Within the json file we can create arrays of information that we can loop through in twig.  Example of array above is the socialLinks info.

Although we can technically create our twig template anticipating drupal's content variables, there may be times when Drupal is not quite ready for us to use its content.  This is where .JSON comes in handy as it gives us a way to populate our component with stock content.

card.js (Github gist):

(function ($) {
  'use strict';


  Drupal.behaviors.card = {
    attach: function (context, settings) {
      var $bioButton = $('.card__view-bio', context);
      var $bioDrawer = $('.card__bio', context);
      var showBio = 'View full biography';
      var hideBio = 'Hide biography';


      $bioButton.on('click', function() {
        $bioDrawer.toggleClass('is-open');
        $bioButton.toggleClass('flip-carat');
        if ($bioDrawer.hasClass('is-open')) {
          $bioButton.text(hideBio);
          $bioButton.toggleClass('is-open');
        }
        else {
          $bioButton.text(showBio);
          $bioButton.removeClass('is-open');
        }
      });
    }
  };
})(jQuery);

Since we are currently working our component for styleguide purposes, in order for card.js, which is a Drupal behavior, to work in our styleguide, we have referenced Drupal Core’s behavior file in our styleguide main template page

(<script src="/core/misc/drupal.js"></script>).  Not doing this would require us to duplicate our javascript, once for Drupal and once for the styleguide.

card.css (Github gist):

Too long to display here but the gist above will give you all the styles needed to theme our component.

A few important things to keep in mind:

  • The social media icons used in this component are being handled by a twig macro.  This is just the way we configure icons.  But you can use any method that works for you.

  • To ensure JavaScript and CSS are only loaded on the components that need it, we create individual libraries that are then attached to the corresponding component twig file.  This lets Drupal handle any dependencies.

I hope this gives you a good idea of building components in Twig to later use them in Drupal.  The next part of this series will focus on getting Drupal configured and ready for integrating our newly built component.