Out of the box, Drupal provides publishers with two basic content types to get started: Article and Basic Page. I have found that Basic Page (machine name: page
) is the most convenient type to use for common page content like About Us and Terms & Conditions pages.
It can be useful to add some additional fields to Basic Page to make the job of creating this content suit your workflow. But when it comes time to theme your pages, Drupal's default theme suggestions can become confusing if you have the need for highly customized page designs.
Drupal's default theme suggestions can become confusing if you have the need for highly customized page designs.
At first, theming your Basic Pages seems straightforward. Drupal creates theme suggestions based on the internal path, and you can easily add a theme template for a page
node by using any of the common naming conventions found in the documentation for various template types. For this discussion, we'll focus on node templates.
For example, let's say you publish your first Basic Page page
node, and the internal path is /node/1
. You have the following template names available.
For the public (non-administration) node template:
node.html.twig // for all nodes
node--viewmode.html.twig // for nodes displayed with a specific view mode
node--page.html.twig // for all Basic Page nodes
node--page--viewmode.html.twig // for all Basic Page nodes displayed with a specific view mode
node--%.html.twig // generic template, more specific than the above
node--1.html.twig // for your specific node 1, all view modes
node--1--viewmode.html.twig // for your specific node 1, with a specific view mode
I've listed the theme suggestions in order from the least specific to the most specific. Drupal will look for the most specific first, and go up the chain to the less specific until it finds a usable template. Make sure to enable theme debugging to see this in action.
Theme debugging in Drupal 7.33+
Theme debugging in Drupal 8.x
Theme Workflow
Let's look at a typical workflow scenario. Let's say you've been contracted to build a Drupal site for a client that sells widgets. Besides the widget content type, you have several common pages to build. These may include:
- About Us page
- Terms of Use
- Copyright policy
- Returns policy
- 404 error page
- 403 error page
You have decided to use the Basic Page content type as a starting point, since it can cover most of your publishing needs. However, the client is very particular about the layouts of these pages, and producing the pages will require unique HTML layouts for each. You will need to produce unique theme templates for each page.
Given the default options provided by Drupal, you can use the Node ID approach, and produce unique templates for each page which may end up looking like this:
node--6.html.twig // About Us page
node--14.html.twig // Terms of Use
node--10.html.twig // Copyright policy
node--33.html.twig // Returns policy
node--68.html.twig // 404 error page
node--69.html.twig // 403 error page
This is a pretty common solution, however the template naming is not very intuitive. It may take some trial and error to find the correct template later. And in the case of the 404 and 403 templates, the layout is typically the same, so they would be copies of one another.
Alternatives to the Node ID approach
Entity View Modes
The module Entity View Modes may be a solution for your Drupal 7 project if your theme template needs are generic enough. (This module's functionality has been included in Drupal 8 Core). Entity View Modes allows you to add additional view modes to the default options "Full Content", "Teaser", "Token", etc. However, this solution would require a way to select what view mode would be needed for each page, which may require an additional module or custom code.
I have found the ability to create custom view modes most beneficial in combination with the Views module (also part of Drupal 8 core), where you can control the view mode of rendered entities in your view's output. I find this useful for displaying content in different locations on a site, and create different view modes accordingly; "home" and "right_sidebar" are two common examples.
Display Suite Extras
The module Display Suite Extras (distributed with Display Suite) does provide the functionality required out of the box. It gives you the ability to create specific view modes for each node. Display Suite is a mature, complex module with many contributors and many active installs, and stable releases for both Drupal 7 and 8, however it does include a lot of other functionality overhead and has a learning curve all its own.
Here is a great tutorial with more information.
Panels
I'm not a fan of the Panels module. Like Display Suite, I think it introduces overhead, a level of complexity and a learning curve that may be too steep for many applications. Once you begin using Panels, all of your content must conform to the Panels way of thinking, and the user interface for publishing is not very intuitive. However, for complex sites where publishers need extreme flexibility to move blocks of content without going back and rebuilding custom theme templates, it may be a good solution.
Here's a good Reddit discussion to give you more insight into Panels.
The Theme Role Solution
If you're a developer, you may prefer a more lightweight custom solution. By adding one field to you Basic Page content type, and a theme suggestions hook, you can accomplish custom theme templates per node.
First, add a new field to your Basic Page content type. The data type should be List (text). The key for each value will be used to call your theme templates. You should make this field required to avoid empty values.
Structure > Content Types > Basic Page > Manage Fields
You then add a theme suggestions hook to your theme to take advantage of the Theme Role values.
Drupal 8 Theme File: themes / THEMENAME / THEMENAME.theme
/**
* Implements hook_theme_suggestions_hook_alter().
*/
function THEMENAME_theme_suggestions_node_alter(array &$suggestions, array $vars, $hook)
{
/** @var \Drupal\node\Entity\Node $node */
$node = null;
if ($node = \Drupal::routeMatch()->getParameter('node'))
{
if ($node->getType() == "page") // Basic Page type
{
$suggestions[] = 'node__page__role__' . $node->field_theme_role->value;
$suggestions[] = 'node__page__role__' . $node->field_theme_role->value . '__' . $variables['elements']['#view_mode'];
}
}
}
In Drupal 7, you'll need to implement template_preprocess_node
Template file: sites / all / themes / THEMENAME / template.php
/**
* ================================================================
* Implements template_preprocess_node
* ================================================================
* @param array $vars
*/
function THEMENAME_preprocess_node(&$variables)
{
if (isset($variables['node'])
{
if ($variables['node']->type == 'page')
{
$variables['theme_hook_suggestions'][] = 'node__page__role__' . $variables['node']->field_theme_role['und'][0]['value'];
$variables['theme_hook_suggestions'][] = 'node__page__role__' . $variables['node']->field_theme_role['und'][0]['value'] . '__' . $variables['view_mode'];
}
}
}
The results are theme templates that are named semantically, making them more intuitive for other team members to find and edit, that can be reused across multiple nodes. Notice that the error
Theme Role has be reused for the 404 and 403 pages.
node--page--role--aboutus.html.twig // About Us page, theme role "aboutus"
node--page--role--terms.html.twig // Terms of Use, theme role "terms"
node--page--role--copyright.html.twig // Copyright policy, theme role "copyright"
node--page--role--returns.html.twig // Returns policy, theme role "returns"
node--page--role--error.html.twig // 404 and 403 error pages, theme role "error"
// additional templates including view mode are also available,
// for example "teaser" for search results
node--page--role--terms--default.html.twig
node--page--role--terms--teaser.html.twig
Conclusion
There are several Drupal module solutions that can provide you with the flexibility to define a custom view mode per node. If you are a developer, the Theme Role solution is an extremely lightweight way to achieve this with minimal overhead and without the need for modules outside of Core.