Published: September 13, 2019
Writing View Mode Templates in PatternLab
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:
<article class="card card--event card--no-image card--image-left"></article>
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:
<h1>Page Title</h1>
<h2>Views Card Block Title</h2>
<h2>Card Title</h2>
<h2>Card Title</h2>
<h2>Card Title</h2>
Instead, we would like this:
<h1>Page Title</h1>
<h2>Views Card Block Title</h2>
<h3>Card Title</h3>
<h3>Card Title</h3>
<h3>Card Title</h3>
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 }}</{{ card_title_element }}>
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:
<h2 class="related-content__title">Read More Like This</h2>
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.