Dripyard Premium Drupal Themes: The challenge of building premium Drupal themes with zero dependencies

Our themes include the ability to install Dripyard custom Drupal recipes from within the theme settings page. To our knowledge, this capability is a Dripyard first and deserves its own blog post (sign up to be notified). To make this work, we utilize the Drupal Batch API, but subsequent batch requests don’t invoke classes defined in a theme since it’s not in context for general Drupal requests. To resolve this, we found a creative way to ensure additional requests invoke our batch process class every time. Using a combination of our class loader and the Batch API’s setFile() method, we’re able to maintain complete control over the batch pipeline to install the recipes.When we started building Dripyard themes, we made the decision early on to not have any dependencies outside of Drupal core. We wanted to avoid depending on contributed modules, npm build processes, and external libraries so that our premium Drupal themes could adapt to any development workflow. Additionally, we wanted to prevent having our own companion module required for Dripyard themes so we can offer a complete package you can download, drop in, and enable. 

Drupal’s autoloader implementation for themes is limited

With the fixes for class loading and object-oriented inheritance, we can establish patterns in our themes—like our RecipeInstaller, which handles the recipe batch processes explained above and allows sub-themes like neonbyte to extend or override select parts of the class. In the neonbyte example, the base class handles all batch processing and the logic of locating and installing items, while the extended neonbyte class defines the recipes that are available and their relationships to other recipes. Additionally, if you use neonbyte as a base theme of your custom theme, the recipes from neonbyte are still discovered and available to be installed form your custom theme settings page. During development of our layout classes, we realized that using our theme namespace Drupaldripyard_base  would sometimes cause errors. For example when Layout Builder was making AJAX requests for the user interface. By ensuring our classes are registered with a custom class loader, we were able to reliably bundle custom Layouts classes in the theme. Thanks to these customizations, we’re able to ship the Dripyard Dynamic Layout with our base theme—a flexible Layout Builder section that lets you define columns, rows, and a wide range of spacing options.

Shipping custom layouts via the Drupal Layout API

Interested in more? Join our webinar where we’ll be launching our themes for sale and you can see the power of Dripyard and Drupal together. Our premium Drupal themes deliver modern PHP, clean architecture, and Layout Builder support—all while depending only on Drupal core!We plan to offer first-class support for Drupal Canvas (formerly Experience Builder) once it has an official release. In the meantime, we’ve standardized on Layout Builder for all of our recipes and demo content, since it’s already part of Drupal core and provides a solid page-building experience.

Batch processing and autoloading

public static function queueInstall($recipe_key, $theme) {
try {
$recipe_path = static::resolveRecipePath($recipe_key, $theme);
$recipe = Recipe::createFromDirectory($recipe_path);
static::$recipeBatch = static::$recipeBatch ?? new BatchBuilder();

// Ensure this file is loaded on every operation since themes
// do not have automatic class loading.
static::$recipeBatch->setFile(Drupal::service('extension.list.theme')->getPath('dripyard_base') . '/src/Recipes/RecipeBatchProcessor.php');

class RecipeInstaller extends RecipeInstallerBase {
/**
* {@inheritdoc}
*/
protected function getAvailableRecipes() {
return [
'dripyard_neonbyte_blocks' => [
'machine_name' => 'dripyard_neonbyte_blocks',
'title' => t('Neonbyte Blocks'),
'description' => t('This recipe provides a set of block types based on the single directory components of this theme. These work well with layout builder, but can be used with other page layout modules.'),
'extended_by' => ['dripyard_neonbyte_demo_content', 'dripyard_neonbyte_landing_pages'],
],
....

A cleaner .theme file in our themes


/**
* Implements hook_form_FORM_ID_alter().
*/
function dripyard_base_form_system_theme_settings_alter(&$form, FormStateInterface $form_state) {
$theme = $form_state->getBuildInfo()['args'][0];
$theme_settings_classes = ClassDiscovery::getAvailableClasses($theme, 'ThemeSettings');
foreach ($theme_settings_classes as $class_name) {
$class = ClassDiscovery::loadClass($theme, 'ThemeSettings', $class_name);
if ($class !== NULL) {
$settingsClass = new $class();
$settingsClass->setTheme($theme);
$settingsClass->themeSettingsFormAlter($form, $form_state);
}
}
}

Inheritable and default theme settings

In this article, we explore the hurdles we faced along the way and how we dealt with them.For Dripyard themes, using Composer is an option, not a requirement. However, we wanted to make use of modern PHP and PHP autoloading to avoid a “spaghetti mess” of hooks and if statements in large .theme files. Drupal core has limited support for PSR-4 autoloading in theme directories, and classes aren’t always loaded like you’d expect, especially when just dropping folders in a ./themes directory. To solve this, we implemented a custom class loader to help with autoloading and to make our DripyardBase namespace consistently available. We did this by creating a dripyard-classloader.php that invokes spl_autoload_register when necessary. There we define our namespaces and perform additional class discovery for other Dripyard themes and sub-themes. We also have an internal discovery class that resolves the inheritance tree of themes that reference ours as a base class

See Dripyard’s core-only approach in action

Even after  handling these edge cases, it should be noted that our classes are still only available when the theme is in context. For example, these classes aren’t loaded in the admin theme—unless you use Dripyard as your admin theme, which we ensure does work if needed.

Similar Posts