Component design for Drupalers

·

5 min read

The cause

Looking jealously at your frontend developers colleagues? They use the newest frameworks and talk about es6, components all the stuff. There are also skilled frontend developers among Drupal programmers and they're interested in stucturing Drupal themes in their own, logical way.

Historical background

A few years ago during the frontend revolution begun after Angularjs - version 1 - the components appeared. They were in Vue, they appeared inside React. The way the web development staff thinks about the code changed. I could spot the upcoming changes even earlier but didn't know how to call it at that time. I saw the pea.rs site (unactive today - but we have Github Repo ), and atomic design emerged. They were all there.

Present

Recently I've been involved in a new project (with Drupal of course) where the decision to choose the frontend way of structuring things was open. The staff talked about the pattern lab precisely. As a backend(maybe?) developer I googled for few minutes and found that there is a solution for that in Drupal. At first sight Drupal is the PHP CMS which could be considered another dusted script but thanks to the lively community, there's always something new to find (GraphQL, Gutenberg etc. - you name it) and there are plenty of integrations to use from thousands of modules. I had chosen few repos and compared them in terms of activity. It appeared that Emulsify was the best shot. And we stayed with it. Frontend developers which usually complain about theming in Drupal were also satisfied.

Emulsify

Emulsify website has all the installation instructions you need. If you're familiar with the composer, you can install it in minutes and create the clone of the Drupal Emulsify theme using the command line. This is where you start the theming adventure.

The Creators were so generous, that the directory is nicely structured and most of the usual Drupal components including form elements has been covered.

screenshot-github.com-2021.08.18-21_40_10.png

Analyze what's inside and you'll immediately recognize what is an atom, molecule or organism.

Get ready - program

As a newcomer to component-based design and really backend guy, who hadn't that much experience with Twig(can it be?) I had to scratch my head few times to understand the basics. That's why this post is being created.

Twig as you may already know is the most popular theming template engine in the PHP world. It helps to decouple business logic from view and is easy to understand for designers and programmers. It's crucial to use just a few functions and make components work like a casket inside a casket construction. And I hope this post will help you here.

A component

The basic element could be a paragraph or element, or link. Let's take a look at the link. Taken from Github :

<a
  {{ bem(link_base_class, link_modifiers, link_blockname) }}
  {% for attribute, value in link_attributes %}
    {{ attribute }}="{{ value }}"
  {% endfor %}
  href="{{ link_url }}"
>
  {% block link_content %}
    {{ link_content }}
  {% endblock %}
</a>

Using component directly

You can use it directly by including it in your content. This is a draft example:

{% @include "@atoms/links/link/link.twig" with {
  link_url: "/contact",
  link_content: "Link to here"
} %}

As you can see - WITH is used to pass the data to the component. Remember this structure, you will use it often.

Getting components together

Now as you have more complicated components, you can use existing components inside them.

<div  {{ bem(card__base_class, card__modifiers, card__base_class) }}>
  <div  {{ bem(card__body__base_class, card__body__modifiers, card__body__base_class) }}>
    {% block card__body %}
    {% enblock %}
  </div>
  <div  {{ bem(card__link__base_class, card__link__modifiers, card__link__base_class) }}>
    {% @include "@atoms/links/link/link.twig" with {
      link_url: card__link,
      link_content: card__link__content
    } %}
  </div>
</div>

You see it's easy to pass certain values with WITH and add data to link inside card component. But what if you want to reach the card__body block? You can use the twig embed function. Its goal is to override blocks.

{% embed "@molecules/card/card.twig" with {
  card__link: "/contact",
  card__link__content: "Link to here"
} %}
  {% block card__body %}
    My custom content from controller maybe.
  {% endblock  %}
{% endembed %}

The possibilities are endless, you can put components inside the component and more components inside. During the development, I never saw problems with nesting too much. But for the sake I keep the same rules as in my SCSS files, not to nest more than 3 elements.

Using modifiers

And as a bonus a little study case. We had a component which is a button and it had no URL but had nice styles that we could use for the links and make the button redirect. Do we have to write again the same style for the link - not. Just use the modifiers cleverly.

Do you remember this piece of code?

{% set link_base_class = link_base_class|default('link') %}
...
<a 
{{ bem(link_base_class, link_modifiers, link_blockname) }}
...

It's a clever helper function that helped us reach the goal of transforming the link into a button. Let's assume that all the styling we need is in the button component and the class for the button is simple .button Here's what has happened:

{% set link_base_class = 'button'; %}
{% set link_modifiers = ['primary', 'primary-outline'] %}

{% @include "@atoms/links/link/link.twig" with {
  link_url: "/contact",
  link_content: "Link to here"
} %}

Now our link will get the right styles of a button.

It will go from

<a class="link"...

to

<a class="button button--primary button--primary-outline"...

What now?

I've enjoyed the component-based design in Drupal. I think thanks to my experience with it I'm able to convince others to work in this way. The stack became modern for the frontend developers and Twig behaves now a little like the frontend frameworks I know.

Bibliography

Here are some precious links for you to expand on the issue of component approach: