Drupal 7: Implementing designs & patterns using Paragraphs

March 26, 2018


I know the sun is setting on Drupal 7, but I think it's still got a few good years left and someone may find this useful.

Design systems and pattern libraries are all the rage now. If you're on Drupal 7 and using Paragraphs, you may be wondering how you can go about implementing patterns into Drupal for editors or your site builders to take advantage of.

Here's the end result of the blog post:

I'm not going to get into what CSS I used, but in this post I'll walk through my process and share some insights of what I learned along the way in regards to implementing the Paragraphs.

Start with a concept

Internally, we use Adobe XD to prototype ideas. A designer on our team analyzed different higher ed sites to come up with common patterns that seem to show up. She then documented those components in an XD document along with some variations.

I took those concepts and built out some pages using different combinations of components and their variations. I got some quick feedback from the team and we decided that I should begin implementing the patterns.

Break down the concept

Try to find common patterns that can act as your Paragraphs container. This ideally should be done after the concept is complete, but you may find that you need to make a couple passes at this.

You can do this on a whiteboard or a piece of paper or within a graphics program or prototyping tool. Doing this could save you a little time. I tried to break it down in my head and ran into some issues that probably could've been avoided.

In discussing the components with other people, questions about how similar one component was to another lead me down a path where I realized where I had 2 components, I could actually get away with only 1 component with a variation option (more on that later, but I used the Classy Paragraphs module)

Getting it into Drupal

For this example I'm going to implement 3 Paragraphs types (components) including the ability to choose some options when utilize the components to introduce variety.

  • Full width section container (full_width_section_container)
    • Background options (field_background_options)
      • Image
      • Green
      • Gold
      • Light gray
    • Spacing options (field_spacing_options)
      • Add vertical margin
      • Add margin top
      • Add margin bottom
    • Background image (field_oz_media)
    • Full width content (field_full_width_content)

  • Small feature (a component that is just an image with text to the side of it and buttons if desired)
    • Title (field_title)
    • Image/Video (field_oz_media)
    • Text (field_accompanying_text)
    • Buttons (field_buttons)
    • Variations (field_variations)
      • Center
      • Move right
      • Image on right

  • Button (button)
    • Title
    • URL
    • Variations (field_variations)
      • Green
      • Light
      • Arrow

Once you get your components broken down into a format like is shown above, it's just a matter of clicking a few buttons within Drupal to create the Paragraphs types.

Reminder: Make sure you go to "Manage Display" and hide all the labels

Protip: Use the fences module to remove Drupal 7's plethora of divs (divitis)

Protip: Use the classy paragraphs module to easily provided a dropdown list that can be used to add classes for use in your $classes array in .tpl.php files.

Add logic within a custom module

I made a module called "paragraphs_pages" to store the logic and templates of the components.

function paragraphs_pages_classy_paragraphs_list_options($options, $field, $instance) {
if ($instance['field_name'] === 'field_background_options') {
$options['bg-lg'] = t('Light gray background (soft)');
$options['bg-gr'] = t('Green background (strong)');
$options['bg-ye'] = t('Gold background (loud)');
}

if ($instance['field_name'] === 'field_spacing_options') {
$options['mv5'] = t('Add vertical margin');
$options['mb5'] = t('Add margin bottom');
$options['mt5'] = t('Add margin top');
}

if ($instance['field_name'] === 'field_variations') {
if ($instance['bundle'] === 'small_feature') {
$options['center'] = t('Center text');
$options['right'] = t('Right side');
$options['img-right'] = t('Image on right side');
}

if ($instance['bundle'] === 'button') {
$options['btn-green'] = t('Green');
$options['btn-white'] = t('Light');
$options['btn-arrow'] = t('Text with arrow');
}
}

return $options;
}

/** * Implements hook_theme(). */
function paragraphs_pages_theme($existing, $type, $theme, $path) {
$theme = array();
$theme['paragraphs_item__small_feature'] = array(
'render element' => 'content',
'base hook' => 'entity',
'template' => 'paragraphs-item--small-feature',
'path' => drupal_get_path('module', 'paragraphs_pages') . '/templates',
);

$theme['paragraphs_item__full_width_section_container'] = array(
'render element' => 'content',
'base hook' => 'entity',
'template' => 'paragraphs-item--full-width-section-container',
'path' => drupal_get_path('module', 'paragraphs_pages') . '/templates',
);

$theme['paragraphs_item__button'] = array(
'render element' => 'content',
'base hook' => 'entity',
'template' => 'paragraphs-item--button',
'path' => drupal_get_path('module', 'paragraphs_pages') . '/templates',
);

return $theme;
}

For the hook_classy_paragraphs_list_options() that the classy paragraphs module provides, I check to see what the field name of the classlist is and provide options that can be selected. If you'd like a UI for this, check out classy paragraphs ui module.

If I want the variations to differ based on what Paragraphs type is being used, I can check the bundle before setting the options, as shown above.

For the hook_theme() implementation, you'll see that I am telling Drupal to use custom templates for our paragraph types that reside within the 'paragraphs_pages' custom module folder.

A template implementation example

full-width-section-container.tpl.php
<?php $bg = (isset($content['field_oz_media'])) ? "filter-darken cover" : ""; ?>
<?php if (isset($content['field_oz_media'])): ?>
<?php $imgurl = file_create_url($content['field_oz_media'][0]['#item']['uri']); ?>
<?php hide($content['field_oz_media']); ?>
<?php endif; ?>

<?php if ($bg === ""): ?>
<div class="pv5 <?php print $classes; ?>"<?php print $attributes; ?>> <?php print render($content); ?> </div>
<?php else: ?>
<div class="pv5 <?php print $bg . " " . $classes; ?>"<?php print $attributes; ?> style="background-image: url('<?php print $imgurl; ?>')">
<div class="relative z-1"> <?php print render($content); ?> </div>
</div>
<?php endif; ?>
small-feature.tpl.php
<?php if (isset($content['field_oz_media'])): ?> <?php $classes .= " has-media"; ?>
<div class="small-feature <?php print $classes; ?>">
<div class="the-media">
<?php print render($content['field_oz_media']); ?>
</div>
<div class="small-feature__content">
<h3><?php print render($content['field_title']); ?></h3>
<?php print render($content['field_accompanying_text'\]); ?>
<?php print render($content\['field\_buttons'\]); ?>
</div>
</div>
<?php else: ?>
<div class="small-feature <?php print $classes; ?>">
<div class="small-feature\_\_content">
<h3><?php print render($content\['field\_title'\]); ?></h3>
<?php print render($content\['field\_accompanying\_text'\]); ?>
<?php print render($content\['field\_buttons'\]); ?>
</div>
</div>
<?php endif; ?>

I know it isn't best practice to put any logic within a tpl.php file but hey, it got the job done.

Most of the classes that I'm using are provided by Tachyons. I find that this framework allows me to rapidly get my components 90% complete. I would highly recommend taking a look at that for quickly building components.

Wrapping up

That is the meat of how to implement some components that can be put into Paragraphs for site builders and editors to create awesome looking, consistent pages using components. Adding just a few variations can help create a large number of combinations of components to create many different looking pages that all have a shared style.