Published: Fri 13 Sep 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.

  1. {% set classes =
  2. [
  3. "card",
  4. content_type ? "card--" ~ content_type,
  5. image ? "card--has-image" : "card--no-image",
  6. alignment ? "card--" ~ alignment,
  7. ]
  8. %}

This will print:

  1. <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:

  1. .card__date {
  2. color: $c-grey--darkest;
  3. }
  4. .card--event .card__date {
  5. font-weight: $fw--bold;
  6. }

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:

  1. {% block image %}
  2. {{ content.field_date }}
  3. {% 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:

  1. <h1>Page Title</h1>
  2. <h2>Views Card Block Title</h2>
  3. <h2>Card Title</h2>
  4. <h2>Card Title</h2>
  5. <h2>Card Title</h2>

Instead, we would like this:

  1. <h1>Page Title</h1>
  2. <h2>Views Card Block Title</h2>
  3. <h3>Card Title</h3>
  4. <h3>Card Title</h3>
  5. <h3>Card Title</h3>

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

  1. {% set card_title_element = card_title_element|default('h2) %}
  2. <article>
  3. <{{ card_title_element }}>{{ card_tite }}</{{ card_title_element }}>
  4. </article>

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:

  1. {% if node._referringItem.parent.parent.entity.field_p_rc_title.value %}
  2. {% set teaser_title_element = 'h3' %}
  3. {% 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:

  1. {% if content_type == 'event' %}
  2. {% if event_location %}
  3. <div class="card__location">
  4. {{ card_location}}
  5. </div>
  6. {% endif %}
  7. {% endif %}

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

If you like what I write, you might like to follow me on Twitter @markconroy

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.