Drupal 7: Implementing designs & patterns using Paragraphs

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.

Using different template (tpl.php) files for a specific content type in Drupal 7

Use case

In one of our sites, we post email communications that were sent out as all-campus announcements on our website.

The problem is, our basic web template does not look like an email template.

So I thought, “I wonder if I could just load a separate set of tpl.php files based on the content type.” Spoiler alert: I could.

I wanted a solution that didn’t require hacking my original theme. I wanted to leave the current theme untouched and simply provide new html.tpl.php and page.tpl.php for this specific content type (presidential_email).

Start with Features

This step is technically optional. If you don’t have a local or dev environment then you don’t need to package your content type in a feature. But for those of us who have to push code through dev -> stg -> prod, features is the best route so you don’t have to rebuild the content type on each environment.

Create your content type in your local environment. Add fields to your liking. Now export that content type using Features.

I typically use the “Generate Feature” functionality under Advanced Options, but you can do whatever you want. Generate Feature allows me to write the code directly where I want to. In this case, it was sites/all/modules/custom.

Fire up your code editor and add your hooks

Go to your sites/all/modules/custom directory and you should see a folder in the path you specified with your content type exported into a Features module.

Add your hooks to your modulename.module file to really get cookin’!

hook_theme

In your hook_theme implementation, you want to specify the new tpl files you want to use. In my case, I want this module to override html.tpl.php and page.tpl.php for the presidential_email content type.

/**
 * Implements hook_theme().
 */
function presidential_email_theme($existing, $type, $theme, $path) {
    $theme = array();
    $theme['html__presidential_email'] = array(
        'render element' => 'content',
        'base hook' => 'node',
        'template' => 'html--presidential-email',
        'path' => drupal_get_path('module', 'presidential_email') . '/templates',
    );
    $theme['page__presidential_email'] = array(
        'render element' => 'content',
        'base hook' => 'node',
        'template' => 'page--presidential-email',
        'path' => drupal_get_path('module', 'presidential_email') . '/templates',
    );
    return $theme;
}

Create your templates folder and files
In your created module folder, add a templates folder. The above functions are telling Drupal to look in yourmodule/templates for html–presidential-email.tpl.php and page–presidential-email.tpl.php.

hook_css_alter

Depending on your use case, this could be optional. In my case, I didn’t want ANY of the typical CSS to be loaded because I wanted to use inline styles created by the MailChimp template. To achieve this, my hook_css_alter looked something like this:

/**
 * Implements hook_css_alter().
 */
function presidential_email_css_alter(&$css) {
    $node = menu_get_object();
    if ($node && $node->type == 'presidential_email' ) {
        foreach ($css as $k => $v) {
            if (!strpos($k, "navbar")) {
                unset($css[$k]);
            }
        }
    }
}

Essentially, this code checks to see if the current node is a ‘presidential_email’ and then loops through all the CSS files, unsetting them all unless it’s necessary for the navbar (I still want it to look presentable while logged in).

hook_preprocess_html and hook_preprocess_page

Last but certainly not least, this method only works if you have auto-generated template suggestions based on node type. For instance, consider my template_preprocess_html and template_preprocess_page functions (replace template with your theme name).

You could also put these in your .module file changing ‘template’ in the function name to your module name.

/**
 * Implements template_preprocess_html().
 */
function template_preprocess_html(&$vars) {
    $node = menu_get_object();
    if ($node && $node->nid) {
        $vars['theme_hook_suggestions'][] = 'html__' . $node->type;
    }
}

/**
 * Implements template_preprocess_page().
 */
function template_preprocess_page(&$variables) {
    if (isset($variables['node']->type)) {
        $nodetype = $variables['node']->type;
        $variables['theme_hook_suggestions'][] = 'page__' . $nodetype;
    }
}

I wrote these at different times in my Drupal career but they do the trick. A content type of “presidential_email” will now produce template suggestions for html–presidential-email.tpl.php and page–presidential-email.tpl.php.

With this in place, the module will now load templates in the module’s template folder when the presidential_email content type is being displayed. Worked well for my simple use case!

How to do a basic accessibility audit on your site

Here are some tools and methods for running an accessibility audit on your site. This can be a little bit easier if you already have a design system or component library in place, but it isn’t necessary to gain some insights into how your site can be more accessible.

The way I usually do it is I first run some automated accessibility checkers, then I do some manual testing. I’ll unpack what this means below.

Note: most of this information is a summary of the W3C’s WCAG site, which is the authority on recommendations regarding web content accessibility. The content on that site can be a little dense so hopefully this provides a good summary and jumping off points.

Free automated accessibility checkers

One thing that is important to remember when using automated tools is that they are not perfect. Just because your site “passes” an automated check doesn’t mean that it is accessible. Also if your site has errors, that doesn’t necessarily mean that it is inaccessible.

Web accessibility evaluation tools can not determine the accessibility of Web sites; they can only assist in doing so.
Source

That being said, it is still quite valuable to run automated checkers on your sites because they can assist in catching errors before manual testing takes place.

One of the more popular automated checkers is WAVE by WebAim. This site allows you to enter a URL and then get a live preview of your site with highlighted accessibility issues in there. It certainly isn’t perfect because it can report some false positives, but it’s a great way to catch obvious issues and contrast errors.

Image of accessibility audit on my site through WAVE.
WAVE’s accessibility audit

Further reading

Manual testing

Here is where the “real” accessibility testing comes in. I say “real” because these methods are actually emulating how users with disabilities actually use your site. Manual testing also can help find gaps in your automated testing.

Keyboard navigation

A basic principle of WCAG 2.0 recommendations is that your site should be operable using nothing more than a keyboard. Give it a shot. Run through your site using your keyboard by pressing tab. Many users with limited motor control rely on keyboard navigation to navigate web sites.

  • Can you access all the links in a logical order?
  • Does advanced functionality like carousels work with keyboard controls?

Reading up on the tabindex attribute can provide enough know-how to make your site way more accessible.

Using voiceover or screen readers

I’m on a Mac so it was quite eye-opening for me to browse sites using the built in Voiceover functionality that comes with all Macs. Using Voiceover shed some light on accessibility principles that I never really understood properly.

For example, we all know the basic rule-of-thumb that alt text is good to use on images. But when using Voiceover, alt text can actually hinder understanding of certain elements such as images with text over them.

Consider this simple image block:

Image block example with text overlay

This image block has alt text of “Student at computer,” which most accessibility novices would assume is a good thing. However, Voiceover reads this element as “Link, Online MBA ranks highest in state student at computer.” This is less than ideal because it is actually less meaningful than if it had alt=””. In situations like this, screen readers tack on alt text to the text that is in the element. By using alt=”” in these situations you can be sure that screen readers will obtain the true meaning of the element.

This is a great example of something that can only be caught using manual testing.

In closing

This was a very brief overview of some tools, resources, and techniques to test your site against WCAG 2.0 accessibility recommendations.

To take it to the next level, there is no substitute for reading the WCAG 2.0 site and browsing through the quick reference provided by the W3C on how to meet the recommendations.

Happy auditing!

Launching a college news site with Drupal

SUNY Oswego recently switched from a legacy install of Expression Engine to integrating with the rest of our web platform and using Drupal hosted by Acquia to “re-design” our new News site. Hopefully this post will help others thinking of migrating to Drupal and provide some helpful tips if you’re already in the process of doing so.

Our old site

Even though our old news site was in Expression Engine, the power of it being a PHP/MySQL app wasn’t fully utilized.

Now, this is through no fault of our office, it just hasn’t been looked at in a very long time. The old site worked well enough for what we needed so we left it alone until an opportunity presented itself to give it a little update.

As a result, the homepage was consistently static. Photos and images were dumped into one WYSIWYG text blob which meant it was difficult to display teaser photos or do anything that is possible with structured content.

Photo captions were placed at the bottom of the articles, separated from their photos. Tagging had no specific function other than creating a word cloud that kinda looked cool. Categories weren’t maintained and difficult to browse by. Finding a list of just all news ordered by date was a hard task to complete.

 

Moving forward

In order to move forward and utilize Drupal to the fullest extent, we planned out content types, audited our workflow, and looked for areas that could be improved as we moved into Drupal.

Auditing our content types

First we sat down and looked at what kind of content was being created. We had a few different content types that were regularly published. News story, People in action, and Spotlight. All of this content was published using one content type and tags to differentiate them in the system.

For the new news site, we now have the same three as before, plus a new Media mention content type. I separated each piece of content into different content types. This allows us to style the content differently and add different fields depending on what the content type needs. The content types are:

  • News story
    • This is the bread and butter of the news site. Most of the content being entered is a news story about something awesome happening on campus.
  • People in action
    • This content type is for faculty, staff, and students who are doing cool things but it’s not necessarily enough to make an entire story or Spotlight out of it.
  • Spotlight
    • This is for highlighting faculty, staff, and students in an in-depth in a Q&A format.
  • Media mention
    • When a news outlet picks up one of our stories, we log it in this content type and display it on the news site to show how our word of our college is spreading.

Social media sharing

When we looked at our analytics of the old site, it wasn’t surprising to find that a large portion of our traffic was when something was shared to Facebook or Twitter. This made the decision easy to invest some resources into:

  • Making sure that the site is easily sharable
  • Making sure the teasers look good on accounts

Metatag module to the rescue

The Drupal metatag module was a YUGE help in making sure that our teasers looked good when shared on social media. If you’re using Drupal, this module is a no brainer.

Beautiful social media teasers, made possible by content strategy and the metatag module.

Google news sitemap module

Another must-have module is the Google news sitemap module. This ensures your articles and content are in a format that is easily consumable by Google. This allows our news stories to be indexed by Google news.

How our news stories appear in Google news search results
How our news stories appear in Google news search results

Human-centered workflow

Last but certainly not least, massive improvements were made to the editorial experience. Drupal gives a site maintainer/developer a tremendous amount of control over the editorial experience, which I tried to leverage as much as possible to make the site easy to update and maintain.

Tabs for each type of content

I’ve referenced this technique in another post and I can’t recommend it enough for improving the editorial experience. By cloning some administration views, we gave editors a super easy way to look at all content of a certain type. This helps reduce cognitive load, making editors more efficient.

Our news admin content interface
Our news admin content interface

I can’t tell you how many times I’ve logged into Drupal with a task in mind, and by the time I figure out which admin area I need to go to, I’ve forgotten what I was doing. Small shortcuts like this technique can reduce these errors and make people’s lives a little better.

Make common tasks as easy as possible

One of the tasks that our news team needs to do is to switch out our featured story once or twice a week, depending on what is happening on campus. I tried to make this as painless as possible, understanding that our writers are not web people. They’re writers. They shouldn’t need to be a web genius to change the featured news story.

Using a combination of views and nodequeue, I added a column to the “News Stories” content tab that allows our senior editor to simply click a link to make that story the featured story on the home page.

Using Views and Nodequeue, a link was added to our news stories admin list to make switching the featured story as easy as clicking a link

Make content guidelines visible

For each of the different types of content, guidelines were developed to ensure that the content would look great once no matter where it is syndicated or in what context it is used.

To help editors know what character count they are at for the summary, I wrote a small module that counts characters for the summary field. As you can see from the screen shot, even the greatest technical solution is at the whim of people and process.

Syndication

A major feature of this news site is now the ability to syndicate news stories from the news site to academic department and college sites through the use of tags and AJAX.

We worked on a list of tags for editors to use that would allow them to syndicate a piece of content to a particular site based on what it was tagged. When implementing, I ensured the field had auto-complete in the add/edit form so we could reduce the number of slightly duplicate tags (for example, Physics department / Physics Department).

Example of news syndication to the Chemistry department website from our News site.
Example of news syndication to the Chemistry department website from our News site.

In closing: a note on people, process, and technology

As with any technology-related project, there are three components that lead to a successful product. People, process, and technology. People and process are vital, and also the most variable.

Sitting down with people to talk about their process is incredibly helpful when building the technical solutions. Often times when the technical team sits down to discuss process, the people who are closest to the process can’t really nail down what it is. That’s where it’s our job to ask questions and try to figure out how things work. This is absolutely necessary if we’re going to turn around a specific, concrete system that the process fits in.

It’s certainly a challenge and it’s never going to be perfect. People change and processes change. Good communication and trust between teams is the only way to get as close to the ideal as we can.

For this iteration, I think we did a really good job. The site is leaps and bounds better in regards to surfacing the latest goings-on at SUNY Oswego. Also, the editor experience is much better as pointed out by our writers who actually use the system (win!).

Crafting a design system for higher education

As a front-end developer at a University, I can see first hand why design systems can be such an integral part of an organization’s communication (and marketing) strategy. This post aims to explain the reasons why large organizations, including higher education, can benefit from having a robust design system in place.

What process is like before a design system

At the University I work at, there are talented designers spread all over campus working for various departments. The offices and departments that are fortunate enough to have a designer working for them utilize their designer’s talents to make flyers, PDFs, and web sites. These designers all have their own style, their own tone, and their own voice. Certain departments use Arial and Helvetica for their typography on print pieces while other departments use Whitney and Sentinel.

It doesn’t take a branding expert to see why this is problematic. All parts of the communications and marketing stakeholders need to be rowing in the same direction in regards to branding.

For most other offices and departments, there are no designers on staff. This means that someone who isn’t necessarily trained in web or design is now responsible for the office’s web site or flyer design. Often times there aren’t enough tools available for this person to create a nice web site and what ends up happening in some cases is that it becomes easier to create a Word document or InDesign file and put a link to that on their site. Or content is simply copied on to the website from a Word document.

I don’t blame offices and departments around campus for doing the best they can with the limited resources they have, but there’s got to be a better way…

Hypothesis: a design system can empower existing designers and lower the barrier for non-technical web editors

It’s often easier to blame your web editors or departments for being difficult to work with or not technically capable. In higher ed, we seem to think we’re a little special. We think we have such a unique situation that no industry solutions could possibly work. Often times this leads to a web team trying to support all their institution’s web sites and all the sites look a little different and are structured a little differently. This leads to an apathetic web team who is constantly putting out fires.

To be fair, I can understand the apathy because it can seem like a dauntingly huge problem to solve. However, this doesn’t seem like a sustainable solution for the long term. There needs to be a process, a plan, in place that allows for quickly building a branded website within the organization. It needs to be so easy that an intern, administrative assistant, or anyone really whose job isn’t web. If it doesn’t work for them, the website will fail because these are the people who are updating the website.

What if there was a way to empower existing designers in the college to all be on the same page? What if we provided tools to web editors in departments that don’t have a lot of technical literacy to make their jobs as easy as possible?

 

The first step to answering this question is to get your ducks in a row and build a design system.

This is what we’re attempting to discover at work and it’s a pretty fun process! To learn more about what a design system actually is, check out my post on the matter.

After a design system is in place, developers can integrate components into whatever CMS you’re using. If the developers are any good, they can also help craft editorial experiences that empowers non-technical users to create on-brand components using your CMS.

What’s the difference between style guides, pattern libraries, and design systems?

In my organization, we’re trying to implement an integrated marketing plan which will be helped along greatly by a sound design system. In order to implement this properly, a shared vocabulary needed to be created so we could be sure we were referring to the same thing. In this post I do my best to explain the terms style guides (editorial and visual), pattern libraries, and design systems.

What a design system consists of
What a design system consists of

A design system forces your organization to think about people and process before a technology solution is introduced.

I think there is a lot of confusion over what exactly a design system is. In my handy Venn diagram, I’ve included what I think a design system consists of. Note that the size of the circles don’t correspond to importance.

Principles are your organization’s foundational ideas that your editorial style guide, visual style guide, and pattern library will live by. These are the ideas that ideally should be running through your organization and be expressed in the style guides and pattern library.

For our design system, I’ve taken this area a bit further and aim to include our main governance policies for our most strategic sites and marketing assets. Most of the principles will be abstract concepts, but some specificity certainly can’t hurt and can provide the organization with a public facing place to review web policies. This is especially important for large, decentralized organizations.

Editorial style guide is exactly what it sounds like. It’s the preferred editorial style for web writing. Including things like whether to use AM/PM or a.m./p.m. when referring to time as well as agreed upon vocabulary for the institution make an editorial style guide useful. If some sites are using “dorms” and some sites are using “residence halls” to describe on-campus housing, a standard should be picked and added to the editorial style guide. Overall voice and tone is also an important piece of an editorial style guide.

Visual style guide is what most designers would call a “branding guide” back in the day. Well crafted branding guides would typically include some editorial guidelines, including voice and tone, whereas a visual style guide only focuses on the core visual pieces of an institution. Items like logo usage, colors, color combinations, typography, and spacing are pillars of a visual style guide. These are the areas of the brand that won’t often change and should be referred to often. To use Brad Frost’s analogy of the store front/workshop, this is the store front. The pattern library is the workshop.

Pattern library is where all the components that are available live. Ideally these patterns are created using the core tenets of the visual style guide such as proper spacing, typography, and colors. A pattern library is far more dynamic than the visual style guide. The web is never finished, new needs are always being created, and therefore new patterns may need to be created to serve those needs. Pattern libraries are where designers can go for inspiration and download code samples. Pattern libraries are where the rubber meets the road in your design system.

You may notice in my Venn diagram above, that I have the visual and editorial style guides overlapping with the pattern library. It is because it’s impossible to completely separate patterns from visual style and editorial style.

Your pattern library is a place where stakeholders can see the fruits of the design system in action. If there are strong editorial and visual guidelines, a pattern library can really shine and stakeholders can see components that can be used across the organization that comply with the brand.

Playing around with the Drupal 8 JSON API as a data service

I’m familiar with the concepts of REST but have never done anything in practice before. I decided I wanted to end that and start messing with the JSON API in Drupal 8. It was surprisingly easy to get going with because of the incredible resources out there. In this multi-part series, I’m going to explore how to use Drupal 8 as a data back-end for a React app.

JSON API Drupal Playlist by Mateu Aguiló Bosch

This video series is an excellent job of succinctly explaining how the JSON API can be used to make basic requests to Drupal.

Postman is amazing

In the series, Mateu is using a tool called Postman (free!), that makes managing REST requests really easy. This is amazing for testing out your API before you hook it up to an application.

Using Postman, for requests that require authentication, you can set up basic authentication (which uses your Drupal username and password) for easy testing purposes.

The examples below were all run with Postman. I decided it would most likely be useful to someone if I showed how I POSTed data to a content type using the JSON API.

Potential gotcha
Make sure in your request headers, you are specifying the Content-Type as application/vnd.api+json. This is according to the JSONAPI spec. Not doing so will throw a 422 Response from the server.

My app use case

I’m building a workout routine React application. The main idea is that users have Routines which contain Timed Tasks (could be yoga postures, jumping jacks, whatever). The Routines hold references to the Timed Tasks. The user can create a Routine of Timed Tasks, and then “play” through the routine, which will prompt the user to perform a workout action.

Timed Task content type

  • Title
  • Body
  • Task Setup
    • Minutes
    • Seconds
  • Task Duration
    • Minutes
    • Seconds
  • Task Rest
    • Minutes
    • Seconds

Routine content type

  • Title
  • Body
  • Timed Task entity reference

Now, lets use Postman to test some basic REST calls that I’m sure my React app will have to make.

Examples:

Local site url: http://drupal-8-3-0.dd:8083

Adding TimedTask content

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task

Body

{
   "data":{
      "type":"node--timed_task",
      "attributes":{
         "status":true,
         "title":"Half Moon right side",
         "body":{
            "value":"This pose is tough",
            "format":"basic_html"
         },
         "field_setup_minutes":{
            "value":0
         },
         "field_setup_seconds":{
            "value":30
         },
         "field_task_minutes":{
            "value":1
         },
         "field_task_seconds":{
            "value":0
         },
         "field_rest_minutes":{
            "value":0
         },
         "field_rest_seconds":{
            "value":20
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--timed_task",
    "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
    "attributes": {
      "nid": 56,
      "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
      "vid": 56,
      "langcode": "en",
      "status": true,
      "title": "Half Moon right side",
      "created": 1491878234,
      "changed": 1491878234,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491878234,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "This pose is tough",
        "format": "basic_html",
        "summary": null
      },
      "field_rest_minutes": 0,
      "field_rest_seconds": 20,
      "field_setup_minutes": 0,
      "field_setup_seconds": 30,
      "field_task_minutes": 1,
      "field_task_seconds": 0
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d1dd56a7-af44-4c01-931d-b441da7f40dc"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/revision_uid"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task"
  }
}

There’s a lot of stuff in there that we don’t need, but this piece is of interest:

"attributes": {
  "nid": 56,
  "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
  "vid": 56,
  "langcode": "en",
  "status": true,
  "title": "Half Moon right side",
  "created": 1491878234,
  "changed": 1491878234,
  "promote": false,
  "sticky": false,
  "revision_timestamp": 1491878234,
  "revision_log": null,
  "revision_translation_affected": true,
  "default_langcode": true,
  "content_translation_source": "und",
  "content_translation_outdated": false,
  "path": null,
  "body": {
    "value": "This pose is tough",
    "format": "basic_html",
    "summary": null
  },
  "field_rest_minutes": 0,
  "field_rest_seconds": 20,
  "field_setup_minutes": 0,
  "field_setup_seconds": 30,
  "field_task_minutes": 1,
  "field_task_seconds": 0
}

The core of the TimedTask is structured nicely for me to use in a front end framework like React.

Now, I want to be able to store a collection of these TimedTasks in a collection known as a Routine. A Routine will consist of many TimedTasks that can be rearranged.

I added 3 more Timed Tasks and noted their UUIDs in the response.

  • ad4a8609-1b98-436c-9efd-fc9fe91ad908
  • 390de987-11b2-47b4-a692-3c160bccab0a
  • 760a6c82-b0f1-4b2a-93d4-140061e878a9
  • 6548f256-a518-491a-8bba-d469778a3722

Now, lets add these as entity references to a Routine content type.

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/routine

Body

{
   "data":{
      "type":"node--routine",
      "attributes":{
         "langcode":"en",
         "status":true,
         "title":"My Yoga Routine :)",
         "body":{
            "value":"<p>This is my morning workout :D</p>\r\n",
            "format":"basic_html",
            "summary":""
         }
      },
      "relationships":{
         "field_timedtasks":{
            "data":[
               {
                  "type":"node--timed_task",
                  "id":"ad4a8609-1b98-436c-9efd-fc9fe91ad908"
               },
               {
                  "type":"node--timed_task",
                  "id":"390de987-11b2-47b4-a692-3c160bccab0a"
               },
               {
                  "type":"node--timed_task",
                  "id":"760a6c82-b0f1-4b2a-93d4-140061e878a9"
               },
               {
                  "type":"node--timed_task",
                  "id":"6548f256-a518-491a-8bba-d469778a3722"
               }
            ]
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--routine",
    "id": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
    "attributes": {
      "nid": 62,
      "uuid": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
      "vid": 62,
      "langcode": "en",
      "status": true,
      "title": "My Yoga Routine :)",
      "created": 1491879173,
      "changed": 1491879173,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491879173,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "<p>This is my morning workout :D</p>\r\n",
        "format": "basic_html",
        "summary": ""
      }
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d50564ec-fb5e-4031-866a-38e8ca98dc0c"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/revision_uid"
        }
      },
      "field_timedtasks": {
        "data": [
          {
            "type": "node--timed_task",
            "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908"
          },
          {
            "type": "node--timed_task",
            "id": "390de987-11b2-47b4-a692-3c160bccab0a"
          },
          {
            "type": "node--timed_task",
            "id": "760a6c82-b0f1-4b2a-93d4-140061e878a9"
          },
          {
            "type": "node--timed_task",
            "id": "6548f256-a518-491a-8bba-d469778a3722"
          }
        ],
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/field_timedtasks",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/field_timedtasks"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine"
  }
}

Using Postman, I helped validate the basic API structure that my React app will use. Next, I’m going to look into how we can hook this up into a React application.

Starting a Vimeo embed at a given timestamp using the JS API

Vimeo by far provides the best hosted video embed service out there by far, in my opinion. This post explains how to change the behavior of a Vimeo embed to skip to a given timestamp when the user plays the video. We’ll use data attributes to store the start time in the HTML.

Why would I want this?

In my situation, the college I work at runs a robust news site that often times includes videos. In that context, we format our videos to include an opening branding screen, then a brief intro paragraph to orient people to the video they are about to watch. This works great for the news site, but what if we come across a situation where we want to skip that intro and go right into the video?

Vimeo Player JavaScript API to the rescue

The Vimeo Player JS API allows developers to easily interact with their Vimeo embeds. The directions on including it are pretty straight forward, they even offer a CDN hosted version to include:

<iframe src="https://player.vimeo.com/video/208129791" width="500" height="281" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>

Next, we look for our Vimeo Player in our JavaScript. In my example, our Vimeo videos are put into our site as follows:


<div class="video" data-start-time="5">
  <iframe src="https://player.vimeo.com/video/204427616?color=498957&title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

So, to access this Vimeo embed, in my JavaScript file I do something like:

if ($('div.video').length) {
  
  // Loop through all the videos
  $('div.video').each(function( index, value ) {

    // Grab the Vimeo player already on the page
    var vPlayer = new Vimeo.Player($(this).find('iframe')[0]);

    // Set a flag to determine if the player has been paused yet
    var pause = false;

    vPlayer.on('play', function() {

      // If the player has not yet been paused, start it at
      // the specified time indicated in data-start-time
      if (!pause)
        vPlayer.setCurrentTime($(value).attr('data-start-time'));
      });

    vPlayer.on('pause', function() {
      // Set the pause flag to true. Setting this prevents
      // the player from starting the video at 
      // the 'data-start-time' instead of just 
      // continuing the video where it was paused.
      pause = true;
    });
  });
}

Limitations

There’s nothing to stop people from rewinding back to the beginning and watching the intro if they choose, so if you’re trying to use this method to cut some video, it won’t work.

This also assumes you have the means to add data attributes to your HTML to store your start times. If you don’t have access to your HTML, you might need to rethink your approach.


Worked perfectly for my needs, hope it helps!

Embedding Drupal Views with the Paragraphs module

The Paragraphs module has been super helpful to developers and site builders who want to provide content editors with a solution to create amazing looking sites without having to know HTML.

I’m currently using Panels/Panelizer and Fieldable Panels Panes in the Drupal build at work to allow content editors to create the pages they want. Panels overall is great but I’ve come across situations where the In-Place Editor is buggy and/or creates confusion for users. The editing experience is a little wonky for them because between the node edit form and the In-Place Editor, there’s too many ways to skin a cat.

So, with our new department sites roll-out, I’m trying something new and moving away from Panels and going all in with Paragraphs. An immediate roadblock I ran into was How do I embed a View in my paragraphs page?

Viewreference module + Paragraphs

After asking around in Drupal IRC, a nice fellow pointed me in the direction of Viewreference. Once I came across this module, things started falling into place.

At a glance
  1. Install module
  2. Create a Paragraph bundle called “Embed view”
  3. Add a Viewreference field to the bundle
  4. Viola! Embed your views.

Install module

drush en viewreference -y

or download, unpack and upload to your module’s folder.

Create a paragraph bundle called “Embed view”

Add a view_reference field to the bundle


Now you can embed Views within a Paragraph bundle!

The result:

Follow up on scalable, maintainable CSS

I wrote down some ramblings about this subject a while back after first reading about it through @mrmrs on Twitter. The idea of single function CSS classes was weird and seemed unnatural at first, but I was very intrigued. I considered this idea around the same time I was realizing that, at work, I basically keep creating variations of the same designs over and over. Thus bloating our CSS file and wasting time developing components that were pretty much previously completed. Then a lightbulb went off. 💡

We were in the middle of a project at work and I decided to try something. I’d create a bunch of “helper” classes that would perform single functions on an HTML element. For instance, .text-center would align the text in the center of the element. .container-800 would create a container that was 800px wide. I created about 20 or 30 of these classes and tried to come up with rules that I routinely use when creating BEM or SMACSS style components.

The result? Incredibly fast iteration! I could piece together these classes in HTML to make interfaces in our CMS so quickly that I couldn’t deny there is some real value in this.

Another result: people who work on my team can no go forth and build awesome interfaces without necessarily needing to know about SCSS, preprocessors, etc. It lowers the barrier for entry and helps groups work together faster.

It makes designer and developers speak the same language. “I think there should be a little more padding on this container” can now be remedied by the designer or the developer by modifying the padding class on the element. If the designer doesn’t know CSS or any of the tooling the developer uses, it is still possible for the designer to change the interface to their liking. This is so powerful.

If you’d like to check out a library that has created tons of these “helper” classes already, look into Tachyons. It’s a wonderful concept and library.