Specbee: Starterkit Theme in Drupal 10: Implementing a Better Starting Point for your Theme

Starterkit Theme in Drupal 10: Implementing a Better Starting Point for your Theme Ashutosh Ahirwal 06 Dec, 2022 Subscribe to our Newsletter Now Subscribe Leave this field blank

Frontend developers and themers have some exciting news coming up with the release of Drupal 10. The new starterkit theme is almost here! Although it has been available for testing since Drupal 9.5.

Starting with the basics, let's talk about themes. Themes are basically the foundation of the entire layout and design of your Drupal website. It is the layer of the website which is seen by end users and it comprises design components like color palettes, font, headers, footers, and other aesthetics of the site.

Now, sub-theming has been a core part of Drupal for a very long time. Sub-theming is the process of inheriting a parent theme’s resources. Creating a sub-theme in Drupal has generally been a manual process by typically inheriting from a core theme (Classy) or any contributed theme to use readily available markup and styling. Drupal Starter kit will completely change the perspective of sub-theming for developers. Read on to find out how and also learn to implement the new Starterkit theme.

Image removed.

Why use the Starterkit Theme

Drupal has been providing the ability to subtheme for a while now. But recently we all have noticed that the Classy theme has not been receiving any updates since Drupal 8.0.0 because it needs to maintain backward compatibility and any changes made might break it. 

When you sub-theme a core theme, it uses the common markup and CSS of the parent theme. And if your Drupal site is dependent on a base theme like Classy, you will not have very few options to make any changes because Classy needs to retain backward compatibility. For that reason, the new concept of a Starterkit theme has been introduced in Drupal 10 core.

Features of the New Starterkit

Drupal has introduced Starterkit in branch 10.0.x and the version is 10.0.0-beta1. It has replaced Classy which is now a contributed project.

  1. As the name suggests, Starterkit will act as a starting point to generate themes. It does not need to be extended as a base theme but rather copied onto the new theme.
  2. Drupal will provide frequent updates on the default markup and CSS in core so that these features will help Front-end developers.
  3. Sub-theming will still be an existing option to create subthemes. This is important in cases where themes are inheriting some common CSS and markup from the base Drupal theme.
  4. If a theme is already using the classy theme, it can continue doing so with the contributed classy theme, which will be the same as using the core Classy theme.
  5. You can generate a new theme using the command line interface tool.

Generating a new theme using CLI

Run this command in the respective (Drupal, docroot, web) folder to create a theme inside the theme folder.

php core/scripts/drupal generate-theme my_new_theme

Add your theme name in place of my_new_theme.

Example: 

php core/scripts/drupal generate-theme test

On running this command, the theme is generated outside of the custom folder.

Image removed.

 

So to generate themes inside the custom folder, you will need to add the path of the file folder like so:

php core/scripts/drupal generate-theme test --path themes/custom

The output will look like this:

Image removed.

 

To see all configuration options, you can reference the help:

php core/scripts/drupal generate-theme --help

Customizing the Starterkit

Starterkit tools provide the freedom to use a contributed or custom theme as the parent theme. For this, you need to add source_theme_name in the command line (which can you get from --help) and then add starterkit: true; in the theme in which you want as create a starter theme or sub-theme. (Remove starterkit: true; after generating the theme)

So finally, the command will look like this:

php core/scripts/drupal generate-theme my_new_them --path [path_of_file_folder] --starterkit source_theme_name 

Example:

php core/scripts/drupal generate-theme demotheme --path theme/custom --starterkit bartik

And the theme will look like this:

Image removed.

 

This theme is generated using Bartik theme and can be checked in the info.yml file of the theme

You can change the generated theme name, code, files, etc according to the requirements.

Tracking upstream changes

When the theme is generated, you need to check for any upcoming changes in the Starterkit core, especially about features, bug fixes, etc. This is also important when you are using starterkit as your base theme

  • Check the version of the core Starterkit theme

When you have generated a theme from Drupal core you need to check the version of the starterkit. You can check for it in the info.yml file in the generator key.

generator: starterkit_theme:9.3.0

With this you can now compare the version of the theme using Git or Drupal core repository.

Syntax

git diff

Example: 

git diff 9.3.0 9.4.0 core/themes/classy/
  • Check the list of theme changes by issue

If you find too many issues then you can review the changes from the list. Check the list of issues by using the below command:

Syntax

git log

Example: 

git log 9.4.0 9.3.0 core/themes/classy/

Final Thoughts

There’s so much to look forward to in Drupal 10 with the Starterkit being a significant enhancement, especially for frontend developers. Enhancements and problem-solvers like the Starterkit proves that Drupal is a truly continuously evolving CMS and is on the right path to making it easier to adopt. As a purely Drupal development company, you can trust our experts to implement Drupal in the best way possible for your next project. We’d love to talk!

Author: Ashutosh Ahirwal

Meet Ashutosh Ahirwal, a Drupal Front-end Developer who is a big-time foodie and a travel enthusiast. Ashutosh is a night owl who fancies going on a long bike ride to Leh-Ladakh, admiring the nature around. He strongly believes there is no such thing as “too much cheese” :)

Drupal Development Drupal 10 Drupal Planet

Leave us a Comment

 

Recent Blogs

Image Image removed.

Starterkit Theme in Drupal 10: Implementing a Better Starting Point for your Theme

Image Image removed.

Things you need for a Drupal 8 to Drupal 9 Upgrade - A Checklist

Image Image removed.

Getting Started with Layout Builder in Drupal 9 - A Complete Guide

Want to extract the maximum out of Drupal? TALK TO US

Featured Success Stories

Image removed.

Upgrading and consolidating multiple web properties to offer a coherent digital experience for Physicians Insurance

Image removed.

Upgrading the web presence of IEEE Information Theory Society, the most trusted voice for advanced technology

Image removed.

Great Southern Homes, one of the fastest growing home builders in the United States, sees greater results with Drupal 9

View all Case Studies

Talking Drupal: Talking Drupal #376 - Burnout

Today we are talking about Burnout with Jono Bacon.

For show notes visit: www.talkingDrupal.com/376

Topics
  • What is burnout
  • Why is it so important to you
  • Have you suffered from burnout
  • Do different professions have different rates of burnout
  • Is it individual or teams / projects / community oriented
  • Is it only mental or can it be physical
  • What contributes to burnout as a contributor or maintainer
  • What can prevent burnout
  • How do you recover
  • First episode was Talking Drupal #265
  • Helping communities
  • Signs to watch out for
  • What is next
Resources Guests

Jono Bacon - www.jonobacon.com @jonobacon

Hosts

Nic Laflin - www.nLighteneddevelopment.com @nicxvan John Picozzi - www.epam.com @johnpicozzi Leslie Glynn - @leslieglynn

MOTW Correspondent

Martin Anderson-Clutz - @mandclu Token The Token module provides a centralized API for text substitution. Since Drupal 7 some Token support is built into core, but the module provides common and reusable token UI elements and missing core tokens.

Salsa Digital Drupal-Related Articles: GovCMS Mega Meetup wrap up

Image removed.December 2022 GovCMS Mega Meetup It was fantastic to be part of the December 2022 GovCMS Mega Meetup, the first in-person GovCMS event for 3 years. It was a great community turnout, and attendees were clearly very engaged.  John Sheridan kicked the day off by announcing a major milestone…over a billion page views hit since GovCMS was launched 2015.  Next, Sharyn Clarkson took to the stage and presented on the ‘great spike’, showing attendees stats on some of the traffic GovCMS sites had during the pandemic. She focused most of her presentation on the GovCMS Roadmap.  Two of the major points on the roadmap are: Rules as Code (RaC)  CivicTheme for GovCMS  RaC is a space we’ve actively been working in over the past year or so.  We were particularly thrilled to have CivicTheme highlighted.

Droptica: Layout Builder Customization. Creating Custom Layout in Drupal

Image removed.

Layout Builder allows quickly creating website layouts from pre-built components added to sections. By default, Drupal provides four types of sections: one, two, three, and four columns. These columns have a predefined behavior the editors have no control over. Drupal offers the ability to create our own types of sections, so we can tailor them to fit our project. This is the process we'll show you in our article.

How to create a custom section in Layout Builder?

The first and most important step is to define the goals and thus, the list of functionalities that the section should provide. Then it's worth breaking the functionalities into small tasks that can be done in a specific time. The aim of our section will be to provide the ability to add classes to the main section wrapper and to individual regions.

As our base we'll use the template available in the Drupal Layout Builder module, which is the one that's used in the sections available with the module installation. Our task list should include:

  • creating a custom module,
  • the definition of a section in the layouts.yml file,
  • the definition of a template for our sections and the plugin in which we'll embed the logic of adding our classes.

Creating a new module in Drupal

We have to create a standard *.info.yml file, as in every module. For a detailed description please refer to the documentation on Drupal.org.

# filename: custom_layout_builder.info.yml name: Custom Layout Builder sections description: Functionality which extends Layout Builder core_version_requirement: ^9 type: module package: custom dependencies: - drupal:layout_builder

We know what the purpose of the module is because we've defined the required functionality. Therefore, already at this stage, we're sure that the list of dependencies should include at least the Layout Builder module. After defining the info.yml file, it's worth clearing the cache and checking if the module appears on the list. To do this, let’s go to the modules view and search for the module by the title or machine name. We should see our module with a list of required dependencies.

Image removed.

 

As we can clearly see, even though we've provided a dependency to the Layout Builder module only, their list is a bit longer. This is because the Layout Builder module has its own dependency list and it’s inherited by our module.

Image removed.

 

At this stage, it's worth considering the mental health of other developers (or yours, if you come back to this code after a few months) and starting to build its documentation. It's worth beginning with the implementation of the hook hook_help().

Image removed.

 

It's also a good idea to create a README.md file and keep it updated.

Section registration using *.layouts.yml

In order to register a new section, the easiest way is to add the *.layouts.yml file (where * is the machine name of our module). The file should be added in the main folder of the module, where we added the *.info.yml file.

Let's start with defining one section:

# filename: ustom_layout_builder.layouts.yml layout_custom_one_column: # Section main key label: '[CUSTOM] One column' # Section title category: 'Custom layouts' path: layouts/custom_onecol_section # Relative path from the template template: layout--custom-onecol-section # Template name default_region: first icon_map: - [first] regions: # Regions table first: # Region machine name label: First # Region title

After the configuration, when adding sections, we should be able to see our newly defined section.

Image removed.

 

Section template defining

In order to be able to add a section, we still need to add the template whose name and path we've specified. In our case, we need to create the layouts/custom_onecol_section folder inside of which the layout--custom-onecol-section.html.twig file must be placed.

By default, the template will have access to four variables: content, attributes, region_attributes, and settings. If we don't put any block in the section, the content variable will return false after being cast to a boolean value. We can take advantage of this behavior to avoid displaying empty HTML tags. Inside the content variable, we'll find the keys corresponding to each defined region, and inside these regions are the blocks we've added. In the content variable, we'll only find the first key, because that's the only one we've defined. The behavior of content.first when casting to a boolean value is analogous to the behavior of the content variable. We'll use this to not display empty tags.

# filename: layout--custom-onecol-section.html.twig {# /** * @file * Default implementation for a custom layout onecol section. * * Available variables: * - content: The content for this layout. * - attributes: HTML attributes for the layout . * - region_attributes: HTML attributes for the region . * - settings: An array of configured settings for the layout. * * @ingroup themeable */ #} {% if content %} {% if content.first %} {{ content.first }} {% endif %} {% endif %}

After adding the template, we should be able to easily add our section:

Defining the Layout plugin

From the end user's perspective, we haven't done anything so far, because the content editor will only see the new section title with the big [CUSTOM] prefix. This is because the section we've added works identically to the default one, provided with the Layout Builder module (with a small exception: our implementation doesn't add any classes). To change its behavior, we have to implement a new layout plugin.

Base class framework

The class should be in the src/Plugin/Layout folder. It'll be generic enough so that it can be used for any number of regions. The Drupal\Core\Layout\LayoutDefault class contains many base methods and implements the needed interfaces. As not to reinvent the wheel, we can expand it in our class.

# filename: CustomLayoutClassBase.php <?php namespace Drupal\custom_layout_builder\Plugin\Layout; use Drupal\Core\Layout\LayoutDefault; /** * Base class of our custom layouts with configurable HTML classes. * * @internal * Plugin classes are internal. */ class CustomLayoutClassBase extends LayoutDefault { }

Adding configuration options to a base class

One of the requirements is the possibility to select a class for the tag wrapping the section's regions. To achieve this, we have to first override the defaultConfiguration method and add a new configuration option to it.

/** * {@inheritdoc} */ public function defaultConfiguration() { $configuration = parent::defaultConfiguration(); return $configuration + [ 'wrapper_classes' => '', ]; }

Then we should add the ability to specify a value for this configuration option. We can do this by overriding the buildConfigurationForm and submitConfigurationForm methods.

/** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form['wrapper_classes'] = [ '#type' => 'textfield', '#title' => $this->t('Wrapper extra classes'), '#default_value' => $this->configuration['wrapper_classes'], '#description' => $this->t('Extra wrapper classes. Type as many as you want but remember to separate them by using a single space character.'), ]; return parent::buildConfigurationForm($form, $form_state); } /** * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { parent::submitConfigurationForm($form, $form_state); $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes'); }

If there's a need to add form validation, it can be done by overwriting the validateConfigurationForm method. We recommend implementing validation for this field, as classes should comply with the Drupal Community standard. The Html::getClass() method may come in handy in this case.

Using configuration to build sections

The render array is created in the build method, and that's what we're going to overwrite now. If you remember the contents of the template we've added, you probably already know that we add classes to the attributes object.

/** * {@inheritdoc} */ public function build(array $regions): array { $build = parent::build($regions); $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']); $build['#attributes']['class'] = [...$wrapper_classes]; return $build; }

Using the base to create a Layout Plugin

Our class is ready, it's time to use it in a section. To do this, go back to the *.layouts.yml file to declare a new plugin. This is done by specifying the full namespace of the class under the class key.

# filename: custom_layout_builder.layouts.yml layout_custom_one_column: label: '[CUSTOM] One column' category: 'Custom layouts' path: layouts/custom_onecol_section template: layout--custom-onecol-section class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomOneColLayout' default_region: first icon_map: - [first] regions: first: label: First

After introducing the above changes, you may notice that the section form has a new field and that the classes entered in that field are in the correct place in HTML.

Adding the option to select classes for regions

We can already define a class list for the wrapper element in our section. It's time to think about how to create the logic responsible for adding classes to individual sections of our layout. We should take into consideration the extensibility of our base class. That's why we recommend basing the logic of determining and accessing regions on the basis of the getRegionNames() method of the LayoutDefinition class.

1. First, we add one field to our form for each region:

/** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form['wrapper_classes'] = [ '#type' => 'textfield', '#title' => $this->t('Wrapper extra classes'), '#default_value' => $this->configuration['wrapper_classes'], '#description' => $this->t('Extra wrapper classes. Type as many as you want but remember to separate them by using a single space character.'), ]; foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) { $form['region_classes'][$region_name] = [ '#type' => 'textfield', '#title' => $this->t('Extra classes for @region_name region', [ '@region_name' => $region_name, ]), '#default_value' => $this->configuration['region_classes'][$region_name], '#description' => $this->t('Extra classes for the @region_name region wrapper. Type as many as you want but remember to separate them by using a single space character.', [ '@region_name' => $region_name, ]), ]; } return parent::buildConfigurationForm($form, $form_state); }

2. We use a similar loop to write the value:

/** * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { parent::submitConfigurationForm($form, $form_state); $this->configuration['wrapper_classes'] = $form_state->getValue('wrapper_classes'); foreach ($this->getPluginDefinition()->getRegionNames() as $region_name) { $this->configuration['region_classes'][$region_name] = $form_state->getValue(['region_classes', $region_name], ''); } }

3. The last step will be overriding the build method and embedding our classes in the appropriate Attributes class object.

/** * {@inheritdoc} */ public function build(array $regions): array { $build = parent::build($regions); $wrapper_classes = explode(' ', (string) $this->configuration['wrapper_classes']); $build['#attributes']['class'] = [...$wrapper_classes]; foreach (array_keys($regions) as $region_name) { $region_classes = explode(' ', (string) $this->configuration['region_classes'][$region_name]); $build[$region_name]['#attributes']['class'] = [...$region_classes]; } return $build; }

After our latest changes, we should see a new Extra classes for first region field where we can provide a list of classes we want to use.

Keep in mind that the region will only appear if it's not empty. That's why we've added a test block containing the node's title. Let's see if classes are visible in HTML.

Creating different variants of sections

The code was written in such a generic way that adding a section with a different number of regions requires only the definition of the region and template from us. Let's add then a new section containing two regions.

First, we add the definition:

# filename: custom_layout_builder.layouts.yml layout_custom_one_column: label: '[CUSTOM] One column' category: 'Custom layouts' path: layouts/custom_onecol_section template: layout--custom-onecol-section class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase' default_region: first icon_map: - [first] regions: first: label: First layout_custom_two_columns: label: '[CUSTOM] Two columns' category: 'Custom layouts' path: layouts/custom_twocol_section template: layout--custom-twocol-section class: '\Drupal\custom_layout_builder\Plugin\Layout\CustomLayoutClassBase' default_region: first icon_map: - [first, second] regions: first: label: First second: label: Second

And then we prepare the template:

# filename: layout--custom-twocol-section.html.twig {# /** * @file * Default implementation for a custom layout onecol section. * * Available variables: * - content: The content for this layout. * - attributes: HTML attributes for the layout . * - region_attributes: HTML attributes for the region . * - settings: An array of configured settings for the layout. * * @ingroup themeable */ #} {% if content %} {% if content.first %} {{ content.first }} {% endif %} {% if content.second %} {{ content.second }} {% endif %} {% endif %} Image removed.

 

The section should be available now.

Image removed.

 

The configuration form should automatically adjust to the number of regions.

Image removed.

After configuring the form and adding test data, we can see the result of our operation in HTML.

Image removed.

 

The module created in this tutorial is available on our GitHub account.

Layout Builder customization - summary

Layout Builder is a great tool whose API allows for full freedom. As always with Drupal, if you can dream it, you can build it. The example shown in this article is only a small part of what can be achieved. If you're interested in the wider use of the Layout Builder API, it's worth reading about the Bootstrap Layout Builder module.

Do you need custom settings in your system? Check out how we can help you as part of our services related to Drupal development.

Peoples Blog: Usage of Constraints (Validations) on Media Entities in Drupal Application

In this article we are going to see how drupal developers can use the Drupal Constraints to Validate the Media entities. Basically Drupal provides Constraints to do the Validations on the Entities, where Drupal uses the Symfony’s validator and extends with Symfony’s Typed Data API for validating specific Entity field definitions. These constraint validators can be used in different w

Peoples Blog: Usage of Local Php Security Checker for Drupal Applications

In this article, we are going to see how the Local PHP Security Checker library will make people's lives easier during the development & code review process. To make developer life easier, developers look for tools or libraries which can automated security review. Here comes the Local PHP Security Checker library, which checks for any known vulnerabilities in the package dependencies. Th

Peoples Blog: Read Data to Paragraph Template in Drupal Application

In this article we are going to see how to read the dynamic data of the node or entity or field values to the template file, which are specific to the paragraph template. Generally while the Paragraph module is used, default template suggestions given by the paragraph module or the template suggestions provided by the hooks are used and further template design is done. Here’s the article wh

Peoples Blog: How to work with Drupal Paragraphs?

Paragraphs can be used as a way for Content Creation in Drupal. It actually allows the site builders to do their stuff a bit cleanly and for the end users this will be pretty easy to manage the content, meaning people get more control on the Editing side. Paragraphs is one of the  popular modules in Drupal, for handling content. It is more or less very similar to the fields and will provide

Peoples Blog: Usage of PhpStan on Github via Pull Request for Drupal Applications

In this article, we are going to see how some tools & libraries will make people's lives easier during the development and code review process. We have a similar helpful article related to Phpcs, have a check of this. To make developer life easier, developers look for tools or libraries which can automated code review and if needed make any corrections in the code automatically. Here come