WordPress.org

WordPress Developer Blog

Bridging the gap: Hybrid themes

Bridging the gap: Hybrid themes

In this article, you’ll examine WordPress theme types and learn how to add block theme features to a classic theme, blending the best of both to create a powerful and flexible hybrid theme.

Theme types

There are two primary theme types: classic themes and block themes.

An overview of block themes

In January 2022, WordPress 5.9 was released, and with it came the Site Editor. This new functionality allowed developers to create themes entirely from blocks while also allowing users more control over various areas of their sites, such as the header and footer, without having to write a single line of code.

A new method to apply styles globally across the site was also introduced, managed through a theme.json file. Block themes simplify the creation of templates by leveraging a visual, block-based approach, allowing for the design of complex layouts directly in the editor. These features empower non-developers to make site-wide changes quickly and intuitively, while giving developers the flexibility to implement advanced functionality and create highly customizable themes.

An overview of classic themes

Classic themes are traditional PHP, HTML, CSS, and JavaScript based themes that date back nearly two decades to 2005, when WordPress 1.5 first introduced the theme system. They rely primarily on PHP to provide advanced functionality, which makes them a great option for developers who need to create themes with highly customized logic and integrations. Unfortunately, this dependency makes them far less accessible to non-developers as even the smallest of changes often require direct editing to the template files. While classic themes support the block editor, they do not support the Site Editor, which requires all parts of its templates to be block markup.

Key differences

Both theme types have several differences and understanding them is important to help you choose the right theme type for your project. Despite their differences, they also share a lot of functionality that you can gradually adopt over time.

The main differences between the theme types are:

  • Site editor: Block themes are designed for the Site Editor which gives users more control over the look and feel of their templates, a feature that classic themes do not support.
  • Templates: Classic themes leverage PHP files, allowing for highly customized templates through code. Block themes require HTML files made up of block-based markup and can be easily created right in the editor.
  • Global styles: Classic themes have limited support for a theme.json file that can be expanded as more block theme features become available in classic themes.

Despite all the differences in functionality and how users work with each, one key difference makes a classic theme a block theme: the inclusion of a /templates/index.html file. When this file is in place, WordPress will view it as a block theme.

Hybrid themes

Somewhere between a classic theme and a block theme, there’s a third approach that doesn’t get as much attention but is gaining more support with each WordPress release. That is the concept of a hybrid theme, one rooted in the traditional approach of a classic theme but able to leverage most of the modern features of a block theme.

Since WordPress 5.9, more and more block theme features have been made available to use in classic themes, allowing developers to gradually adopt these new features and use a mix of PHP or HTML files without making the full leap into a block theme. This is what is referred to as a hybrid theme, and throughout the rest of this article, each feature will be explored in detail.

Getting setup

To make things easier to follow along, you can clone an accompanying GitHub repository that uses underscores as a starting point. The repository contains a few new files that allow you to quickly test out hybrid theme functionality.

  • functions-hybrid.php
  • /assets/js/hybrid-theme.js
  • /assets/css/hybrid-theme.css

The functions-hybrid.php file includes the two block asset enqueues that load the necessary CSS and JS files. If you are using your own theme while following along, here are the enqueues found in the example repository.

// Enqueue script into block editor
function hybrid_theme_enqueue_scripts() {
 wp_enqueue_script(
     'hybrid-theme-scripts',
     get_template_directory_uri() . '/assets/js/hybrid-theme.js',
     array( 'wp-blocks', 'wp-dom-ready', 'wp-edit-post' ),
     null,
     true
 );
}
add_action( 'enqueue_block_editor_assets', 'hybrid_theme_enqueue_scripts' );

// Enqueue styles in block editor and front end
function hybrid_theme_enqueue_styles() {
 wp_enqueue_style(
     'hybrid-theme-styles',
     get_template_directory_uri() . '/assets/css/hybrid-theme.css',
     array(),
     filemtime( get_template_directory() . '/assets/css/hybrid-theme.css' )
 );
 add_editor_style( 'assets/css/hybrid-theme.css' );
}
add_action( 'enqueue_block_assets', 'hybrid_theme_enqueue_styles' );

Now that you’re ready, let’s explore the block features that can be used in a classic theme and how to set them up.

If you’d like to skip ahead and see the theme with examples the repository has a branch that contains a version of the theme with most of the following examples added.

Global styles

The Global Styles and Settings API was introduced in WordPress 5.8, and developers could start controlling settings, tools, and styles in their themes through a theme.json file. Initially this feature only had partial support for classic themes, but over time, the support has expanded with each new release.

To leverage this feature, simply create a theme.json file at the root of your theme. Here’s an example to get you started.

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "layout": {
      "contentSize": "768px",
      "wideSize": "1024px"
    },
    "color": {
      "palette": [
        {
          "slug": "primary",
          "color": "#DE7D0D",
          "name": "Primary"
        },
        {
          "slug": "secondary",
          "color": "#7C0DDE",
          "name": "Secondary"
        }
      ]
    },
    "typography": {
      "fontFamilies": [
        {
          "slug": "default",
          "name": "Default",
          "fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
        }
      ]
    }
  }
}

In this example, you are adding layout sizes, a basic color palette and setting the default font. There are plenty of resources out there, so I won’t go into detail on what you can and can’t do with a theme.json file, but if you want to explore that more I’d encourage you to check out the resources available in the Theme Handbook.

Block theme supports

There are several theme supports you can add to a classic theme to include both block theme features, and block functionality. Read more about theme supports in the WordPress Developer Resources.

  • responsive-embeds – adds support for responsive embeds
  • editor-styles – adds support for loading a stylesheet in the block editor
  • disable-custom-colors – disables the color picker for blocks
  • disable-custom-font-sizes – disables the custom font size for typography

The following theme supports will not work when a theme.json file is in place (because they can be done in your theme.json file, more about this below).

  • align-wide – adds support for full and wide alignments in the editor
  • custom-line-height – adds typography option to customize line height
  • custom-spacing – adds support for custom padding on blocks
  • custom-units – adds support for custom measurements
  • editor-color-palette – adds support for creating custom color palettes
  • editor-gradient-presets – adds support for creating custom gradient presets
  • editor-font-sizes – adds support for creating custom font sizes for typography
function hybrid_add_theme_supports() {
  add_theme_support( 'responsive-embeds' );
  add_theme_support( 'disable-custom-colors' );
  add_theme_support( 'disable-custom-gradients' );
  add_theme_support( 'disable-custom-font-sizes' );

  // Cannot use alongside a theme.json file
  add_theme_support( 'align-wide' );
  add_theme_support( 'custom-line-height' );
  add_theme_support( 'custom-spacing' );
  add_theme_support( 'custom-units', array( 'rem', 'em', 'px', 'vw', 'vh' ) );

  add_theme_support(
    'editor-color-palette',
    array(
      array(
        'name'  => __( 'Dusty Blue', 'hybrid-theme' ),
        'slug'  => 'dusty-blue',
        'color' => '#367cb3',
      ),
      array(
        'name'  => __( 'Faded Brown', 'hybrid-theme' ),
        'slug'  => 'faded-brown',
        'color' => '#7a6a53',
      ),
    )
  );

  add_theme_support(
    'editor-gradient-presets',
    array(
      array(
        'name'     => __( 'Dusty to Faded', 'hybrid-theme' ),
        'gradient' => 'linear-gradient(135deg, #367cb3 0%, #7a6a53 100%)',
        'slug'     => 'dusty-to-faded',
      ),
    )
  );

  add_theme_support(
    'editor-font-sizes',
    array(
      array(
        'name' => 'Small',
        'slug' => 'small',
        'size' => 12
      ),
      array(
        'name' => 'Medium',
        'slug' => 'medium',
        'size' => 18
      ),
    )
  );
}
add_action( 'after_setup_theme','hybrid_add_theme_supports' );

Theme supports in theme.json

There’s a much easier and more efficient way to add these theme supports. In fact, you already set up a couple of them in earlier sections of this article when you created a theme.json file. The example below adds support for everything listed above except for the responsive-embeds support.

{
 "$schema": "https://schemas.wp.org/trunk/theme.json",
 "version": 3,
 "settings": {
   "layout": {
     "contentSize": "1024px",
     "wideSize": "1280px", // align-wide
   },
   "typography": {
     "customFontSize": false, // disable-custom-font-sizes
     "lineHeight": true, // custom-line-height
     "defaultFontSizes": false,

     // editor-font-sizes
     "fontSizes": [
       {
         "name": "Regular",
         "slug": "regular",
         "size": "16px"
       },
       {
         "name": "Very Big",
         "slug": "very-big",
         "size": "36px"
       }
     ]
   },
   "color": {
     "custom": false, // disable-custom-colors
     "customGradient": false, // disable-custom-gradients

     // editor-color-palette
     "palette": [
       {
         "name": "Primary",
         "slug": "primary",
         "color": "#DE7D0D"
       },
       {
         "name": "Secondary",
         "slug": "secondary",
         "color": "#7C0DDE"
       },
       {
         "name": "Dusty Blue",
         "slug": "dusty-blue",
         "color": "#367cb3"
       },
       {
         "name": "Faded Brown",
         "slug": "faded-brown",
         "color": "#7a6a53"
       }
     ],

     // editor-gradient-presets
     "gradients": [
       {
         "name": "Dusty to Faded",
         "slug": "dusty-to-faded",
         "gradient": "linear-gradient(135deg, #367cb3 0%, #7a6a53 100%)"
       }
     ]
   },
   "spacing": {
     "units": [ "px", "em", "rem", "%" ] // custom-units
   },

   // custom-spacing (done on a per block basis)
   "blocks": {
     "core/columns": {
       "spacing": {
         "padding": true,
         "margin": true
       }
     }
   }
 }
}

Block variations

The Block Variations API was introduced in WordPress 5.4 and provides developers with an easy way to create variations of existing blocks with unique starting attributes. There are a couple of steps to adding a block variation in a classic theme, but they are simple and straightforward.

Add a block variation by pasting the code below into the /js/hybrid-theme.js. In this example, you are creating a variation on the Paragraph block called Intro Paragraph that sets new defaults for the font size, sets the drop cap to true, and uses a unique placeholder.

wp.blocks.registerBlockVariation('core/paragraph', {
   name: 'intro-paragraph',
   title: 'Intro Paragraph',
   description: 'A paragraph with large text and a drop cap.',
   attributes: {
       fontSize: 'medium',
       dropCap: true,
       placeholder: 'Write an introduction.',
   },
});

Add the new Intro Paragraph block variation to a page and you should see something that looks like this screenshot.