WordPress blocks – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Wed, 01 Mar 2023 15:12:49 +0000 en-US hourly 1 https://wordpress.org/?v=6.4.3 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 WordPress blocks – CSS-Tricks https://css-tricks.com 32 32 45537868 How the Style Engine Generates Classes https://css-tricks.com/how-the-style-engine-generates-classes/ Mon, 27 Feb 2023 14:46:43 +0000 https://css-tricks.com/?page_id=376996 The WordPress Style Engine generates the CSS for a block theme. Why would you want to know how an invisible process like that works? Well, just like writing CSS, you will want to ensure your code is organized and structured so that your styles properly use the CSS Cascade.


How the Style Engine Generates Classes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In the last article in this series, we went through theme.json presets and style section configurations. Then we discussed how the WordPress Style Engine processes the JSON into CSS and consolidates it with consolidated data received from different sources — WordPress Core styles, theme styles, and user styles — to create a global stylesheet. We also learned how to apply the generated CSS to individual elements, like headings, and specific blocks, like the Query Loop.

The Style Engine is responsible for structuring and organizing your CSS into a single global stylesheet, and understanding how it parses your theme.json file and styles coming from other sources is rather important. Otherwise, you might find yourself in a situation where you’re unsure why one of your styles isn’t working. This way, you’ll have an idea of where to dig in and troubleshoot without being in the dark.

I discuss the WordPress Style Engine in another article. That’s a great place to start if you’re looking for a little more context without all the nitty gritty details we’re covering here.

Table of contents


Where styles come from

The Style Engine has to get its information from somewhere, right? The truth is that there are several places where the Style Engine looks for information before it generates CSS.

  • WordPress Core: That’s right, WordPress ships with pre-defined styles right out of the box. Think of these as default styles authored at the CMS level, sort of like the default styles in browsers. They are there by default, but we can override them in our theme.json file.
  • The theme.json file: We’ve covered this file thoroughly throughout the series. It’s where we enable certain styling features and define default styles, such as colors, fonts, and spacing. You can think of it like the style.css file in a “classic” WordPress theme: the styles we author at the theme level.
  • User styles: WordPress has a number of features that allow you to set styles directly in WordPress without touching code. The “big” one of which is the Global Styles panel in the Site Editor. It uses the styles from WordPress Core and theme.json as default settings, but changing the style settings overrides the other two sources.

Can you sort of see how this forms a hierarchy? WordPress Core styles can be overridden by theme.json, and both theme.json and WordPress Core styles can be overridden by user styles. It’s structured a lot like the CSS Cascade, in a way!

Combining sources

Just so we have a good picture of what we’re working with, here’s a screenshot of the WordPress Site Editor with the Global Styles settings on display.

WordPress Site Editor screen with the Global Styles settings open and highlighted with a red border.

This is the interface where user styles come from. Notice how there is a tile at the top of the panel that provides a preview of the theme’s current typography and colors. That tile is called a style variation, which is basically preset style configurations that are defined in JSON in the theme folder. A theme can have any number of pre-defined style variations, and choosing one instantly swaps the theme’s fonts and colors with the selected tile.

So, already, you can see how the user styles are informed by theme.json. But notice the three categories of style settings below the style variations: Typography, Colors, and Layout. Clicking into any of those settings allows you, as the user, to override the configuration of the currently selected style variation.

WordPress Site Editor screen with the Global Styles settings open to color options.

Now you see how theme.json and WordPress Core styles are overridden by user styles. We can lean on WordPress Core styles for basic layout and styling, customize them and define our own styles in theme.json, then let the user take greater control of things with user styles from the Site Editor’s Global Styles settings.

All of this styling data is stored and parsed as JSON. The Style Engine generates that JSON and then consolidates it into a single global stylesheet for the theme.

Processing theme.json

During the consolidation phase, the Style Engine takes a look at the theme.json file located in the theme directory. We talked earlier about the structure of theme.json and how it is organized into two “top-level” sections, settings, and styles.

{
  "version": 2,
  // Top-level settings
  "settings": {},
  "styles": {
    // Global element styles
    "elements": {},
    // Block styles
    "blocks": {}
  }
}

The Style Engine processes those sections like this. First, the top-level settings generate CSS variables that are applied to the <body> element (e.g. --wp--preset--<category>-<slug>):

{
  "version": 2,
  "settings": {
    // Top-level settings
    "color": {
      "palette": [
        {
          "color": "#F8A100",
          "name": "Primary",
          "slug": "primary"
        }
      ]
    }
  },
  "styles": {
    // Global element styles
    "elements": {},
    // Block styles
    "blocks": {}
  }
}

In this example, we get a new CSS variable, --wp-preset--color--primary. The Style Engine then applies it to new CSS classes with a .has- prefix:

body {
  --wp--preset--color--primary: #F8A100;
}

.has-primary-color {
  color: var(--wp--preset--color--primary) !important;
}
.has-primary-background-color {
  background-color: var(--wp--preset--color--primary) !important;
}
.has-primary-border-color {
  border-color: var(--wp--preset--color--primary) !important;
}

Neat, right? These classes can now be used anywhere in the theme!

Next, the styles.elements object generates element selectors matching the HTML element they represent (e.g. styles.elements.h2 corresponds with the <h2> element).

{
  "version": 2,
  "settings": {},
  "styles": {
    // Global element styles
    "elements": {
       "h2": {
        "color": {
          "text": "#F8A100"
        }
      }
    },
    // Block styles
    "blocks": {}
  }
}

In this example, an h2 selector is generated with a #F8A100 color value:

h2 {
  color: #F8A100;
}

And, hey, we could have used the CSS variable we defined earlier in settings as the color value, knowing the Style Engine is going to generate it:

{
  "version": 2,
  "settings": {},
  "styles": {
    // Global element styles
    "elements": {
       "h2": {
        "color": {
          "text": "var(--wp--preset--color--primary)"
        }
      }
    },
    // Block styles
    "blocks": {}
  }
}

…which gives us this in CSS:

h2 {
  color: var(--wp--preset--color--primary);
}

And, finally, the settings.blocks object uses the concatenation of the block and element selector. This way, the specificity for these selectors is higher, giving them greater priority over the other generated styles we’ve seen so far.

For example, let’s change the color of the Site Title block:

{
  "version": 2,
  "settings": {},
  "styles": {
    // Global element styles
    "elements": {},
    // Block styles
    "blocks": {
      "blocks": {
        "core/site-title": {
          "color": {
            "palette": [
              {
                "color": "#F8A100",
                "name": "SF Orange",
                "slug": "sf-orange"
              }
            ]
          }
        }
      }
    }
  }
}

Here’s how that shakes out in CSS. The Style Engine generates a class for the Site Title block (.wp-block-site-title) and for the color as a descendant combinator selector:

.wp-block-site-title .has-sf-orange-color {
  color: var(--wp--preset--color--sf-orange) !important;
}
.wp-block-site-title .has-sf-orange-background-color {
  background-color: var(--wp--preset--color--sf-orange) !important;
}
.wp-block-site-title .has-sf-orange-border-color {
  border-color: var(--wp--preset--color--sf-orange) !important;
}

Generated CSS classes

So, now we have a pretty good idea of how the WordPress Style Engine processes data it receives from various sources into a block theme’s CSS.

We also got to see how the settings and styles from theme.json produce CSS variables and classes. What you’re probably interested in now is what other CSS classes do we get from Style Engine. Let’s dig into those.

Block classes

A “block” in WordPress is a standalone component that can be dropped into any page or post. Every block gets a CSS class that is used as the block’s parent container.

And all of the class names have a wp-block prefix. For example, the Cover block gets a .wp-block-cover class that we can use to select and style the entire block. WordPress calls these semantic classes because they identify the block in the name.

Blocks get another class in addition to its semantic class, called its stateful class. These classes describe the block’s state, as if it “has” a certain text color, or “is” a certain background color or layout type.

For example, let’s say we add a Post Title block to one of our theme’s templates in the Site Editor. But then we change it, so it has a background color, the “SF Orange” color we defined in an earlier example.

The WordPress Site Editor open on the Homepage template and displaying a large bright orange box with Hello World in it in black.

This results in a stateful class on the element. Here’s the HTML:

<h1 class="wp-block-post-title has-background has-sf-orange-background-color">
  <a href="#" target="_self">Hello world!</a>
</h1>

See the two stateful CSS classes this made?

  • .has-background: This adds padding to the element, so the Post Title does not bump the edges of the container, allowing more background to show.
  • .has-sf-orange-background-color: This applies the CSS variable for the color.

Here is a table of selected WordPress blocks and examples of the sorts of classes that are generated and applied to them.

BlockSemantic ClassStateful Class
Buttons.wp-block-buttons.has-custom-width
Cover.wp-block-cover.is-light.has-parallax
.has-position-vertical
Columns.wp-block-columns.has-2-columns
.has-background
Heading.wp-block-heading.has-text-color
Gallery.wp-block-gallery.has-nested-images
Image.wp-block-image.alignleft
.aligncenter
.alignright
.has-custom-border
Spacer.wp-block-spacer.is-style-dots
.has-text-color
Quote.wp-block-quote.is-layout-constrained

Again, this is not an exhaustive table of blocks. You can find a complete list, however, over at the WordPress Block Editor Handbook. If there is a complete list of stateful classes living somewhere, I could not find it. So, while the stateful class examples in the table are accurate based on my testing, it should also not be considered a complete list of classes.

Layout classes

WordPress provides different layout types that can be applied to container-based blocks. By that, we’re talking about the following blocks:

  • Columns
  • Group
  • Post Content
  • Query Loop

Each of these blocks can be assigned a layout type, and there are three options:

  • Flow layout: Adds vertical spacing between nested blocks in the margin-block direction. Those nested blocks can also be aligned to the left, right, or center.
  • Constrained layout: Same exact deal as a Flow layout, but with width constraints on nested blocks that are based on the contentWidth and wideWidth settings (either in theme.json or Global Styles).
  • Flex layout: This was unchanged in WordPress 6.1. It uses CSS Flexbox to create a layout that flows horizontally (in a row) by default but can flow vertically as well, so blocks stack one on top of another. Spacing is applied using the CSS gap property.
The block editor with a two-column layout and the Layout settings open.

Depending on the settings selections, this corresponds to the following CSS classes:

  • .is-layout-flow
  • .is-layout-constrained
  • .is-layout-flex

See how the stateful naming convention is carried over to these? These aren’t the only layout-related classes, though. Here are all of the available classes, as documented in the WordPress Block Editor Handbook:

Justin Tadlock has an excellent article that explains these layout types and semantic classes with use cases and examples. You can also refer to my article, “Using The New Constrained Layout In WordPress Block Themes”, for even more information on using different layouts.

Additional sources of user styles

We already know we can use the Global Styles settings in the Site Editor to override the CSS styles that come from WordPress Core and theme.json. We called those user styles.

But that’s not the only place where user styles can come from. For example, let’s say you’re writing a new post and want to style a specific paragraph a certain way. And let’s say you have a CSS class that you either defined theme.json or perhaps even your own stylesheet! As long as the CSS for those classes is loaded on the page, you can add them to any block in the block’s Advanced settings.

This Add Additional CSS Classes to Blocks guide walks you through using custom CSS classes on your site.

There’s another place where user styles can go. As of Gutenberg 14.8, a new custom “Additional CSS” box has been added to the Global Styles settings.

Source: Make WordPress Core

And, hey, a big heads-up: The CSS from these user-style sources can override or even remove the CSS settings in theme.json. Another big thing to know is that whatever styles you define here could get lost when changing themes. It’s probably better to actually create these styles in theme.json or by enqueuing your own stylesheet.

Block stylesheets

There’s one more place where the WordPress Style Engine might get some styling data, and that’s from your own stylesheets! That’s right: you can add your own stylesheets to a theme.

Sure, you could also use the required style.css file as your stylesheet. Most block themes won’t be using it anyway. But there’s an even more efficient way to go about it: to enqueue stylesheets for specific blocks.

First off, you may already be familiar with the concept of enqueuing files from working with classic WordPress themes and the wp_enqueue_style() function. That loads a CSS file by providing a path where WordPress can find it.

We can do the same thing but on a block-by-block basis. Each WordPress block has its own associated stylesheet, and we can enqueue our own stylesheets for them. For example, here I am adding a stylesheet of custom styles for the Quote block in my theme’s functions.php file:

add_action( 'init', 'emptytheme_enqueue_block_styles' );

function emptytheme_enqueue_block_styles() {
  wp_enqueue_block_style( 'core/quote', array(
    'handle' => 'emptytheme-block-quote',
    'src'    => get_theme_file_uri( "assets/blocks/quote.css" ),
    'path'   => get_theme_file_path( "assets/blocks/quote.css" )
  ) );
}

Check that out — it’s practically the same way we’d load custom stylesheets in a classic WordPress theme, PHP and all.

If you want to add styles to multiple blocks, you need to enqueue CSS files using an array and then loop through them. The WordPress Developer Blog has a nice example written by Justin Tadlock:

add_action( 'init', 'themeslug_enqueue_block_styles' );

function themeslug_enqueue_block_styles() {
  // Add the block name (with namespace) for each style.
  $blocks = array(
    'core/button'
    'core/heading',
    'core/paragraph'
  );

  // Loop through each block and enqueue its styles.
  foreach ( $blocks as $block ) {

    // Replace slash with hyphen for filename.
    $slug = str_replace( '/', '-', $block );

    wp_enqueue_block_style( $block, array(
      'handle' => "themeslug-block-{$slug}",
      'src'    => get_theme_file_uri( "assets/blocks/{$slug}.css" ),
      'path'   => get_theme_file_path( "assets/blocks/{$slug}.css" )
    ) );
  }
}

All of this is in active development!

Please note that the WordPress Style Engine is still pretty new, and the work for it is ongoing. The Style Engine document lists some of the planned upcoming work:

  • Consolidate global and block style rendering and enqueuing (ongoing)
  • Explore pre-render CSS rule processing with the intention of reduplicating other common and/or repetitive block styles. (ongoing)
  • Extend the scope of semantic class names and/or design token expressions, and encapsulate rules into stable utility classes.
  • Propose a way to control hierarchy and specificity, and make the style hierarchy cascade accessible and predictable. This might include preparing for CSS cascade layers until they become more widely supported, and allowing for opt-in support in Gutenberg via theme.json.
  • Refactor all blocks to consistently use the “style” attribute for all customizations, that is, deprecate preset-specific attributes such as attributes.fontSize.

You can track the development status on the GitHub project board.

Even with these limitations, this tweet from Rich Tabor and the following video demonstrates the unlimited opportunities we have for customizing the appearance of a block theme — without even touching code!

⚠️ Contains auto-playing media
Cycling through various theme styles.

All made possible, thanks to the WordPress Style Engine and its JSON parsing superpowers.

Additional resources

We covered a lot of ground in this article! I thought I would share some of the resources I relied on for the information.

Documentation

Tutorials

GitHub issues

A lot of the context for this article comes from proposals and issues reported to the WordPress GitHub repo.

Next up…

We’re actually all done! But I’ve created a page that pulls all of what we learned about CSS in WordPress Block Themes and provides you with a one-stop place you can reference anytime you need it.


How the Style Engine Generates Classes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376996
WordPress Global Styles Reference Tables https://css-tricks.com/wordpress-global-styles-reference-tables/ Tue, 31 Jan 2023 17:41:00 +0000 https://css-tricks.com/?page_id=376849 We’ve covered a lot of ground in this series. So much so that I thought it would be helpful to condense all the various block theme settings and styles from theme.json into a single page right here.


WordPress Global Styles Reference Tables originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The following information is based on the WordPress documentation for ease of reference only. At the time of writing, Global Styles (theme.json) and user interface customizations are being actively developed. And, of course, all of this is still very much in active development. If you notice something has changed or is incorrect, please let me know in the comments!

Table of contents


Settings

{
  "version": 2,
  "settings": {
    "border": {},
    "color": {},
    "typography": {},
    "layout": {},
    "spacing": {},
    "outline": {},
    "appearanceTools": true | false,
    "useRootPaddingAwareAlignments": true | false
}

We’ll start with the settings configurations, where we have broken this out into multiple tables for Typography, Colors, Custom, and Spacing.

Remember, these “top-level” styles are applied on the <body> element. And it might be helpful to recall the syntax we’re dealing with:

  • CSS Custom properties: --wp--preset--{preset-category}--{preset-slug}
  • CSS classes: .has-{preset-slug}-{preset-category}

Border

The border section enables border controls in the editor UI.

{
  "version": 2,
  "settings": {
    "border": {
      "color": true,
      "radius": true,
      "style": true,
      "width": true
  }
}
JSON PropertyWhat it DoesCSS Equivalent
settings.border.colorEnables the border color control.border-color
settings.border.radiusEnables the border radius control.border-radius
settings.border.styleEnables the border style control.border-style
settings.border.widthEnables the border width control.border-width

Color

The color section sets the theme’s color palettes, gradients, and duotone effects.

{
  "version": 2,
  "settings": {
      "color": {
        "palette": {
          "slug": "",
          "color": "",
          "name": "",
        },
        "gradients": [
          {
            "slug": "",
            "gradient": "",
            "name": ""
          }
        ],
        "duotone": {
          "slug": "",
          "color": "",
          "name": "",
        }
        "link": true | false
      }
    }
  }
}
JSON PropertyWhat it DoesCSS Custom Properties
settings.color.pallete.colorDefines color values for use throughout the theme.--wp–preset--color--{slug}
settings.color.gradients.colorDefines gradient patterns for use throughout the theme.--wp–preset--gradient--{slug}
settings.color.duotoneDefines duotone effects for use throughout the site.–-wp–preset--duotone--{slug}
settings.color.linkEnables the setting to change the theme’s default link color in the Site Editor.–-wp–preset-–color-–link

Typography

{
  "version": 2,
  "settings": {
    "styles": {
      "typography": {
        "dropCap": true | false,
        "customFontSize": true | false,
        "fluid": "<undefined>" | false,
        "fontSizes": [
          {
            "fluid": {
              "min": "",
              "max": ""
            },
           "slug": "",
           "size": "",
           "name": ""
         },
       ],
      "fontFamilies": [
        {
          "fontFamily": "",
          "name": "",
          "slug": "",
          "fontFace": ""
        },
      ],
      "lineHeight": true | false,
      "fontStyle": true | false,
      "letterSpacing": true | false,
      "textDecoration": true | false,
      "textTransform": true | false,
    }
  }
}
JSON PropertiesWhat it DoesCSS Custom Property
settings.typography.fontSizesDefines font size throughout the site.--wp--preset--font-size--{slug}
settings.typography.fontFamiliesDefines typographic font use throughout the site.--wp--preset--font-family--{slug}
settings.typography.lineHeightDefines line height to use throughout the site.--wp--preset--line-height--{slug}
settings.typography.fontStyleDefines font style to use throughout the site.--wp--preset--line-height--{slug}
settings.typography.fontWeightDefines font weight for use throughout the site.--wp--preset--font-weight--{slug}
settings.typography.letterSpacingDefines letter spacing for use throughout the site.--wp--preset--letter-spacing--{slug}
settings.typography.textDecorationDefines text decoration for use throughout the site.–wp-preset–text-decoration–{slug}
settings.typography.textDecoration.lineDefines link text decoration line for use throughout the site.--wp-preset--text-decoration--line
settings.typography.textDecoration.styleDefines link text decoration line style for use throughout the site.--wp-preset--text-decoration--style--{slug}
settings.typography.textDecoration.colorDefines link text decoration color for use throughout the site.--wp–preset--text-decoration--color--{slug}
settings.typography.textDecoration.thicknessDefines link text decoration line thickness for use throughout the site.--wp–preset--text-decoration--thickness--{slug}
settings.typography.textTransformDefines text transformation type (uppercase, lowercase) for use throughout the site.--wp--preset--text-transform--{slug}

Spacing

These settings enable/disable spacing controls for margin and padding in the Site Editor and allow you to set which CSS units to support.

"styles": {
  "spacing":{
    "padding": true,
    "margin": true,
    "units": [ "px", "em", "rem", "vh", "vw", "%" ]
  }
}

The Site and Block Editors include slider form inputs that allow you to set spacing on a scale of predefined values. We can customize that scale with the spacingScale property:

{
  "version": 2,
  "settings": {
    "spacing": {
      "spacingScale": {
        "operator": "+",
        "increment": <number>,
        "steps": <number>,
        "mediumStep": <number>,
        "unit": ""
      }
    }
  }
}
JSON PropertyWhat it DoesExample
settings.spacing.operatorDetermines whether the control scales up or down.+
settings.spacing.incrementDetermines much the spacing changes per step.0.25
settings.spacing.stepsDetermines the number of available steps on the scale.6
settings.spacing.mediumStepSets the midpoint of the range scale.1.5
settings.spacing.unitSets the CSS length unit of the numeric value.rem

One more thing we can do with spacing is create CSS custom properties that can be used throughout the theme:

{
  "version": 2,
  "settings": {
    "spacing": {
      "spacingSizes": [
        {
          "size": "",
          "slug": "",
          "name": ""
        }
      ]
    }
  }
}
JSON PropertyWhat it DoesExample
settings.spacing.spacingSizes.sizeSets the custom size value, including unit.3.5rem
settings.spacing.spacingSizes.slugThe slug used in the CSS custom property name.medium
settings.spacing.spacingSizes.nameProvides the label for the size in the Site and Block Editor UI.Medium

The resulting CSS custom property syntax is: --wp-preset--spacing--<slug>

Layout

{
  "version": 2,
  "settings": {
    "layout": {
      "contentSize": "",
      "wideSize": "",
      "type": "",
    }
  }
}
JSON PropertyWhat it DoesGenerated CSS Property
settings.layout.contentSizeSets the maximum width of the default content container for pages and posts.--wp--style--global--content-size
settings.layout.contentWideSets the maximum width of the“wide” content container for pages and posts.--wp--style--global--wide-size
settings.layout.typeDetermines is the container is default or constrained.

Appearance tools

{
  "version": 2,
  "settings": {
    "appearanceTools": true | false,
}

Setting this to true is a shorthand for enabling the following settings:

{
  "version": 2,
  "settings": {
    "border": {
      "color": true,
      "radius": true,
      "style": true,
      "width": true
    },
    "color": {
      "link": true
    }
    "spacing": {
      "margin": true,
      "padding": true,
      "blockGap": true,
    },
    "typography": {
      "lineHeight": true
    }
  }
}

Root padding-aware alignments

{
  "version": 2,
  "settings": {
    "useRootPaddingAwareAlignments": true | false
}

Setting this to true is a shorthand for enabling the following settings:

{
  "version": 2,
  "settings": {
    "spacing": {
      "margin": true,
      "padding": true
    },
  }
}

Styles

The idea here is that styles are organized by top-level styles (those that set styles on the <body>), global styles (those that set styles on global elements, like headings), and block styles (those that set styles on specific blocks).

{
  "version": 2,
  "styles": {
    // Top-level styles
    // etc.

    // Global-level styles
    "elements": { },

    // Block-level styles
    "blocks": {  }
  }
}

Top-level styles

{
  "version": 2,
  "styles": {
    // Top-level styles
    "border": [],
    "color": [],
    "spacing": [],
    "typography": [],
    "filter": "",
    "shadow": "",
    "outline": []
  }
}
PropertyStyleCSS equivalentAdditional props
bordercolorborder-color
radiusborder-radius
styleborder-style
widthborder-width
topborder-topcolorstylewidth
rightborder-rightcolorstylewidth
bottomborder-bottomcolorstylewidth
leftbottom-leftcolorstylewidth
colorbackgroundbackground-color
gradientbackground-image
textcolor
spacingblockGapgap (in Flexbox and Grid containers)
marginmarginbottomleftrighttop
paddingpaddingbottomleftrighttop
typographyfontFamilyfont-family
fontSizefont-size
fontStylefont-style
fontWeightfont-weight
letterSpacingletter-spacing
lineHeightline-height
textDecorationtext-decoration
textTransformtext-transform
filterduotonefilter
shadowbox-shadow
outlinecoloroutline-color
offsetoutline-offset
styleoutline-style
widthoutline-width

Global styles

{
  "version": 2,
  "styles": {
    "elements": {
      "buttons": {
        "border": {
          "radius": ""
          },
          "color": {
            "background": "",
            "text": ""
          }, 
          "heading": {},  
          "h1": { },
          "h2": { },
          "h3": { },
          "h4": { },
          "h5": { },
          "h6": { },              
          "link": {
            "border": { },
            "color": { },
            "spacing": { },
            "typography": { }
            }
          }
        }
    }
  }
}
JSON PropertyWhat It DoesWhere It Is Used
styles.elements.buttonsDefines button element properties (e.g., bordercolor, etc.) for use throughout the site.Buttons
styles.elements.headingDefines styles of all the headings, the (<h1> to <h6>) elements for use throughout the site.<h1> to <h6> (all)
styles.elements.h1 to styles.elements.h6Individually defines styles for <h1> to <h6> elements of the heading block for use throughout the site.<h1> to <h6> (individually)
styles.elements.linkDefines link <a> element style for use throughout the site.<a>
styles.elements.citeDefines styles for the blockquote.citequote.cite classes for use throughout the site.Quote, Pullquote
styles.elements.captionDefines styles for <figcaption> and <caption> elements for Image and Table blocks, respectively, for use throughout the site.Figure, Table
styles.elements.spacing.paddingSets the padding of headings, row, blocks and paragraph blocks for use throughout the site.Headings, row, blocks, paragraph
styles.elements.typographySets the default typography style for headings and paragraph blocks for use throughout the site.headings, paragraph

Global-level styles: interactive elements

{
  "version": 2,
    "styles": {
      "elements": {
        "button": {
          "color": {
            "background": " ",
            "text": " "
          }
          ":hover": {
            "color": {
              "background": " ",
              "text": " "
            }
          },
          ":focus": {
            "color": {
              "background": " ",
              "text": " "
            }
          },
          ":active": {
            "color": {
              "background": " ",
              "text": " "
          }
        },
        ":visited": {
          "color": {
            "background": " ",
            "text": " "
          }
        }
      }
    }
  }
}

Note: We can use any already predefined JSON properties in settings presets like typography, outline, shadow, etc., to add styling to any global JSON elements.

JSON PropertyStyleWhat It DoesCSS Equivalent
styles.elements.colorBackgroundDefines link background color for use entire site.a:link { background-color }
TextDefines link text color for use entire site.a:link { color }
styles.elements.:hoverBackgroundDefines hover state link background color for use entire site.a:hover { background-color }
TextDefines hover state link text color for use entire site.a:hover { color }
styles.elements.:focusBackgroundDefines focus state link background color for use entire site.a:focus { background-color }
TextDefines focus state link text color for use entire site.a:focus { color }
styles.elements.:activeBackgroundDefines active state link background color for use entire site.a:active { background-color }
TextDefines active state link text color for use entire site.a:active { color }
styles.elements.:visitedBackgroundDefines visited state link background color for use entire site.a:visited { background-color }
TextDefines visited state link text color for use entire site.a:visited { color }

Custom

This table shows examples of custom properties you could make using the Custom section of the theme.json file. The CSS custom properties generated by the Custom section use the following syntax: --wp--custom--<variable-name>

JSON PropertySyntaxGenerated Custom Property
settings.custom.spacing.padding--wp--custom--{padding}--wp--custom--padding
settings.custom.typography--wp--custom--{typography}--wp--custom--typography
settings.custom.fontWeight--wp--custom--{font-weight}--wp--custom--front-weight
settings.custom.lineHeight--wp--custom--{line-height}--wp--custom--line-height
settings.custom.someColor--wp--custom--{some-color}--wp--custom--some-color
settings.custom.someParagraph--wp--custom--{some-paragraph}--wp--custom--some-paragraph

Block-level styles

All the global-level styles including the elements may be used to customize the CSS for individual blocks to overwrite the global customization. Block-level styles have precedence over top-level styles (global).

{
  "version": 2,
  "styles": {
    // Top-level styles
    // etc.

    // Global-level styles
    "elements": { },

    // Block-level styles
    "blocks": {
      "core/<BLOCKNAME>": {
        // Define or overwrite any global styles
        "typography": {
          "fontSize": " ",
          "fontWeight": "",
          "lineHeight": "",
          "letterSpacing": "",
          "textDecoration": "",
          "textTransform": "" 
        },
        // Define or overwrite any global elements styles
        "elements": {
          "link": {
            "typography": {
              "textDecoration": ""
            },
            "color": {
              "text": " ",
              "background-color": ""
            },
            ":hover": {
              "typography": {
                "textDecoration": ""
              },
              "color": {
                "text": "",
                "background-color": ""
              },
            },
            ":focus": {
              "typography": {
                "textDecoration": ""
              },
              "color": {
                "text": " ",
                "background-color": ""
              },
            },
            ":active": {
              "typography": {
                "textDecoration": ""
              },
              "color": {
                "text": " ",
                "background-color": ""
              },
            },
            ":visited": {
              "typography": {
                "textDecoration": ""
              },
              "color": {
                "text": "",
                  "background-color": ""
              },
            },
          },
        },     
      },
      // Additional blocks
      "core/<BLOCKNAME>": {
        "typography": { },
        // etc.
      }
    }
  }
}
JSON PropertyStyleWhat It Does
.styles.core.<BLOCKNAME>.typographyfont-sizeDefines or overwrites the global font size of this block only.
font-weightDefines or overwrites the global font weight of this block only.
line-heightDefines or overwrites he global font height of this block only.
letter-spacingDefines or overwrites he global letter spacing of this block only.
text-decorationDefines or overwrites he global text decoration of this block only.
text-transformDefines or overwrites the global text transformation of this block only.
.styles.core.<BLOCKNAME>.elements.link.typographytext-decorationDefines or overwrites the global link typography of this block only.
.styles.core.<BLOCKNAME>.elements.link.color`background-colorDefines or overwrites the global link background color of this block only.
colorDefines or overwrites the global link text color of this block only.
.styles.core.<BLOCKNAME>.elements.link.:hover.typographytext-decorationDefines or overwrites the global text decoration of hover link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:hover.colorbackground-colorDefines or overwrites the global background color of hover link state of this block only.
colorDefines or overwrites the global background color of hover link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:active.typographytext-decorationDefines or overwrites the global text decoration of active link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:active.colorbackground-colorDefines or overwrites the global background color of active link state of this block only.
colorDefines or overwrites the global background color of active link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:focus.typographytext-decorationDefines or overwrites the global text decoration of focus link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:focus.colorbackground-colorDefines or overwrites the global background color of focus link state of this block only.
colorDefines or overwrites the global background color of focus link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:visited.typographytext-decorationDefines or overwrites the global text decoration of visited link state of this block only.
.styles.core.<BLOCKNAME>.elements.link.:visited.colorbackground-colorDefines or overwrites the global background color of visited link state of this block only.
colorDefines or overwrites the global background color of visited link state of this block only.

For use case examples of block-level CSS customization, you may refer to the latest Twenty Twenty-Three theme and other recent block themes in the theme directory.

Wrapping up

You made it through this compelete guide to WordPress block theme CSS and settings! We started with a brief introduction that compares WordPress block themes to the “classic” PHP templating system. From there, we talked a bit about JSON because, funny enough, that’s how we “write” CSS in WordPress block themes.

Then there’s theme.json the actual file where all of those styles are defined, much like style.css was for classic PHP themes. The file is tightly structured into sections for toggling WordPress settings and defining CSS globally throughout a block theme.

And once we’re done defining a block theme’s settings and styles, the WordPress Style Engine takes over, processing our JSON into a nicely organized set of CSS styles used in the theme.

It’s crazy just how different block themes are when it comes to defining and managing styles in WordPress! All of this is still in active development, and we are likely to see new features and options added to theme.json.

See something that’s new or has changed? Please let me know in the comments!


WordPress Global Styles Reference Tables originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376849
Defining Global Styles https://css-tricks.com/defining-global-styles-in-wordpress/ Tue, 31 Jan 2023 16:16:18 +0000 https://css-tricks.com/?page_id=376823 Let’s move to the other top-level section of theme.json where we can configure the CSS of a block theme: styles. We’ll learn what it is exactly and how we can use it to override and apply the preset settings values we covered in Part 2.


Defining Global Styles originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In Part 2 of this series, we covered the process of enabling certain features in a WordPress block theme in the theme.json file. We spent time in the settings section of the file, outlining the various available features and how they allow us to customize the Global Styles UI in the WordPress Site Editor with preset values that map to CSS custom property values that are scoped to the <body> element.

Table of contents


The styles section (styles)

The styles section sits alongside the settings section we covered in the last article as a top-level section.

{
  "version": 2,
  "settings": {},
  "styles": {
    // etc.
  }
}

We learned in Part 2 that the settings section defines default styles at the top level — the CSS generally applied to the <body> element. The styles section is where we can override those presets with more granular styles applied to specific global elements (e.g. <h1>) and specific blocks (e.g. theme.json file from Twenty Twenty-Three, and other latest block themes from theme directory).

In other words, the settings are “top-level” styles that cascade down to all elements. We can override those presets in the styles section where we apply styles to specific elements and blocks. In fact, elements and blocks are subsections of styles:

{
  "version": 2,
  "settings": {},
  "styles": {
    // Global element styles
    "elements": {},
    // Block styles
    "blocks": {}
  }
}

What makes this great is that theme.json is outlined in a way that organizes styles to cascade very much the same way CSS stylesheet does. If settings is for configuring global style features, then styles is where we override those presets when working with specific global elements and individual blocks.

Supported properties

When we looked at the settings section in Part 2, we saw that it contains “features” that can be enabled and configured in a block theme. Regarding the styles sections, we’re defining CSS properties instead.

In fact, the objects contained in the styles section are mapped to actual CSS properties. For example, styles.border-color corresponds to the border-color property in CSS.

So, before we jump into the styles section and how to customize the appearance of a block theme, we ought to know exactly what CSS we’re working with. The following table outlines all of the CSS properties that are currently supported in the styles section as of WordPress 6.1:

Full table of properties
PropertyStyleCSS equivalentAdditional props
bordercolorborder-color
radiusborder-radius
styleborder-style
widthborder-width
topborder-topcolorstylewidth
rightborder-rightcolorstylewidth
bottomborder-bottomcolorstylewidth
leftbottom-leftcolorstylewidth
colorbackgroundbackground-color
gradientbackground-image
textcolor
spacingblockGapgap (in Flexbox and Grid containers)
marginmarginbottom, leftrighttop
paddingpaddingbottomleftrighttop
typographyfontFamilyfont-family
fontSizefont-size
fontStylefont-style
fontWeightfont-weight
letterSpacingletter-spacing
lineHeightline-height
textDecorationtext-decoration
textTransformtext-transform
filterduotonefilter
shadowbox-shadow
outlinecoloroutline-color
offsetoutline-offset
styleoutline-style
widthoutline-width

There are a couple of things worth noting when it comes to setting these properties:

  • There are no logical property equivalents. For example, margin-left is supported, but margin-inline-start is not as of this writing.
  • Multiple color formats are supported, such as rgba and hsla, but the new syntaxes are not, e.g., rgb(255, 255, 255 / .5).

Top-level styles

We already covered top-level styles in Part 2. By “top-level” we mean styles that are applied globally on the root element (<html>) as well as the <body>. These styles are “top-level” in the sense they are inherited by everything in the theme by default.

The classic example in CSS is setting a default font size on the <body> element:

body {
  font-size: 1.125rem;
}

Every element now has a font-size value of 1.125rem, unless it is overridden somewhere else. You might think that top-level styles are set in the styles section of theme.json. But if you recall the last article in this series, top-level styles are the preset values we define in the settings section instead:

{
  "version": 2,
  "settings": {
    "typography": {
      "fontSizes": {
        "size": "1.125rem",
        "slug": "medium"
      }
    }
  },
  "styles": {}
}

Behind the scenes, the WordPress Style Engine generates a CSS custom property that is applied to the <body> element:

body {
  font-size: var(--wp--preset--font-size--medium);
}

…which results in precisely what we’d expect in CSS:

body {
  font-size: 1.125rem;
}

Now we can jump into the styles section to override this sort of top-level style on two different levels: elements and blocks.

Element-level styles

What if we want to scope that top-level font-size to a specific element? A global element is a little tricky to understand because they are both blocks and blocks that can be nested in other blocks.

Think of elements as “core” blocks. That’s actually what they’re called in the WordPress Handbook, and it’s a good description of what they are: blocks that come with WordPress Core out of the box.

A heading is a perfect example of a core block. There is a Heading block we can use to add any heading level (<h1><h6>) to a page or post. So, if you need to drop a Heading 2 element on a page, you have a specific block to do that.

But a Heading 2 might be part of another block. For example, if you were to add a Query Loop block to a page, you would get a list of posts, each with a Post Title block that outputs an <h2> element.

Query Loop block in the WordPress Block Editor with a Heading 2 element.

If you want to style all heading elements, regardless of level, you can do it in the elements object in theme.json:

{
  "version": 2,
  "settings": { }
  "styles": {
    // Global-level styles
    "elements": {
      "heading": { ... },
    }
  }
}

Let’s say the CSS color property is set to black as a top-level style in settings:

{
  "version": 2,
  "settings": {
    // Top-level styles
    "color": {
      "palette": {
        "color": "#000000",
        "name": "Contrast",
        "slug": "contrast"
      }
    }
  },
}

The WordPress Style Engine creates a CSS custom property that can be applied to the <body> at the top level:

body {
  color: var(--wp--preset--color--contrast);
}

Maybe you want all your headings to be a different color than what is already applied to the <body> element. You can set that on styles.elements.heading to override black with, say, a dark gray color:

{
  "version": 2,
  "settings": {
    // Top-level style presets
    "color": {
      "palette": {
        "color": "#000000",
        "name": "Contrast",
        "slug": "contrast"
      }
    }
  },
  "styles": {
    // Global-level styles
    "elements": {
      "heading": {
        "color": {
          "text": "#333333"
        }
      }
    }
  }
}

Another way to go about it is to configure a new color in settings.color.palette and apply the generated CSS custom property to styles.elements.heading.color.text.

OK, but maybe you want the Heading 2 global element to “pop” more than other heading levels. You can override the dark gray for all the core headings element with another color value assigned to the h2 element:

{
  "version": 2,
  "settings": {
    // Top-level style presets
    "color": {
      "palette": {
        "color": "#000000",
        "name": "Contrast",
        "slug": "contrast"
      }
    }
  },
  "styles": {
    // Global-level styles
    "elements": {
      "heading": {
        "color": {
          "text": "#333333"
        }
      },
      "h2": {
        "color": {
          "text": "#F8A100"
        }
      }
    }
  }
}

At the time of this writing, the following global elements are currently supported in the styles.elements section:

JSON PropertyGenerated selectorUse cases
elements.buttonsButtonsButtons block, blocks
elements.headingHeadingsHeadings block,
elements.h1 to elements.h6<h1> to <h6>Site title, post title, blocks
elements.link<a>Links
elements.citeblockquote.cite, quote.citeQuote, Pullquote
elements.caption<figcaption>, <caption>Figure, Table
elements.spacing.paddingPaddingHeadings, row, blocks, paragraph
elements.typographyTypographyHeadings, paragraph

Block-level styles

There’s yet one more level in styles, and it’s used to customize the CSS for individual blocks:

{
  "version": 2,
  "styles": {
    // Top-level styles
    // etc.

    // Global-level styles
    "elements": { },

    // Block-level styles
    "blocks": {  }
  }
}

Let’s go ahead and pick up right where we left off in the last section. We had set the color property to black in the top-level styles, overrode that with a different color for all headings in styles.elements.heading, then overrode that just for the Heading 2 element on styles.elements.h2. Here’s that code again:

{
  "version": 2,
  "styles": {
    // Top-level styles
    "color": "#000000",

    // Global-level styles
    "elements": {
      "heading": {
        "color": {
          "text": "#333333"
        }
      },
      "h2": {
        "color": {
          "text": "#f8a100"
        }
      },
    }
  }
}

Earlier, we discussed how a global element, like Heading 2, can also be part of another block. We used the Query Loop block as an example, where Heading 2 is used for each post title.

So far, the color of the Query Loop’s post title would be #F8A100 because that is what is set on styles.elements.h2. But you can override that in the styles.blocks section if you want to set the Query Loop block’s Heading 2 element to yet another color without interfering with other headings:

{
  "version": 2,
  "styles": {
    // Top-level styles
    "color": "#000000",

    // Global-level styles
    "elements": {
      "heading": {
        "color": {
          "text": "#333333"
        }
      },
      "h2": {
        "color": {
          "text": "#F8A100"
        }
      }
    },
    "blocks": {
      "core/query": {
        "elements": {
          "h2": {
            "color": {
              "text": "var(--wp--preset--color--primary)"
            }
          }
        }
      }
    }
  }
}

Behind the scenes, the WordPress Style Engine creates the CSS for the Query Loop’s <h2> element:

.wp-query h2 {
  color: var(--wp--preset--color--primary);
}

Pretty great, right? Now we have a way to set default styles and override them at various levels in the theme in a structured way that works much like the CSS Cascade.

Check out the WordPress Handbook for a complete list of blocks that are available to use in the Block Editor.

Interactive styles

What if you want to customize the CSS for different interactive states? Everything we’ve looked at so far is great for styling a core block, like Button, but what about the styles when someone hovers their cursor over a Button block? Or maybe when that button is in focus?

We can do that with CSS pseudo-classes. You’re probably already well-versed in using pseudo-classes like :hover, :focus, :active, and :visited. They’re super common!

Thankfully, support for styling interactive states for certain core blocks, like buttons and links, using pseudo-classes gained theme.json support in WordPress 6.1. Here’s an example pulled from one of my previous articles that adds a box-shadow on a theme’s Button elements on hover:

{
  "version": 2,
  "settings": {},
  "styles": {
    "elements": {
      "button": {
        ":hover": {
          "shadow": "10px 10px 5px 0px rgba(0,0,0,0.66)"
        }
      }
    }
  }
}

The WordPress Style Engine reads this and generates the following CSS:

.wp-button:hover {
  box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.66);
}

But before you rush off to create all your interactive styles, you’ll want to note that theme.json currently supports just the :hover, :focus, and :active interactive pseudo-classes as of WordPress 6.1, and even those are limited to the button and link elements. But that is likely to expand to others in the future — including pseudo-classes specific to form elements — as noted in this Make WordPress Core blog post introducing interactive styles.

So, for now, here is an example of the most complete way to customize the interactive styles of a button.

{
  "version": 2,
  "styles": {
    "elements": {
      "button": {
        "color": {
          "background": "#17A2b8",
          "text": "#ffffff"
        }
        ":hover": {
          "color": {
            "background": "#138496"
          }
        },
        ":focus": {
          "color": {
            "background": "#138496"
          }
        },
        ":active": {
          "color": {
            "background": "#138496"
          }
        }
      }
    }
  }
}

The same thing works if we were to change elements.button to elements.link.

One more JSON object is mentioned in the WordPress Handbook called css but there is no documentation for it other than mentioning it is used to set custom CSS that is not handled by other theme.json properties.

Referencing styles

A referencing style feature also available in theme.json. This allows you to refer to a previously defined root-level style property using the ref: term.

In the earlier top-level style example, we have registered the following background color using the styles.color.background property at the root of the styles property.

"styles": {
  "color": {
    "background": "var(--wp--preset--color--base)"
  }
}

We can reuse the same style property to any number of blocks:

{
  "color": {
    "text": { ref: "styles.color.background" }
  }
}

Global style variations

It’s possible for a theme to include more than one theme.json file. Why? Because that’s how you can make Global Styles variations.

That link will take you to the formal documentation for Global Styles variations, but the general idea is this: theme.json contains all your default values and sits in the theme folder’s root directory, and additional theme.json files are added to the theme folder that are “variations” of the default theme.

Here’s how it works. Say we have finished configuring our default theme.json file and it’s sitting in the theme’s root directory. Now say you want to implement a “dark mode” feature that allows the user to toggle colors between light and dark palettes. We’d make a new file — maybe call it dark.json and place it in a new folder in the theme called /styles. We can add as many of these files as we want to create all the variations your heart desires.

theme-folder/
|__ /assets
|__ /patterns
|__ /templates
|__ /parts
|__ /styles
    |__ dark.json
|__ index.php
|__ functions.php
|__ style.css
|__ theme.json

The biggest difference between the default theme.json file and our dark.json variation file is that our variation file includes a title field that allows us to differentiate it from other JSON files.

{
  "version": 2,
  "title": "Dark",
  "styles": {}
}

Anything we put in here overrides what’s in theme.json once it is activated. And how do you activate a global styles variation? You’ll find it in the Site Editor (Dashboard → Appearance → Editor) under “Browse Styles” (Editor → Styles → “Browse Styles”). That’s the secret behind all the different variations offered in the default Twenty Twenty-Three theme.

The full-site template editing screen with the style variations panel open.

You can read more about building block theme style variations in this CSS-Tricks article.

Additional resources

Wrapping up

We are really close to having a more complete picture of the theme.json file. We’ve covered the structure, how to enable features, and how to configure styles at the theme, global, and block level, even going so far as to look at custom styling capabilities and overriding all of our defaults with global styles variations. Hopefully you see just how powerful theme.json is in the block theme era of WordPress.

The next article in this series will give you a deeper look at the Style Engine. We’ve referred to it several times but have only briefly described what it does. It’s an integral piece of the block theme puzzle because it transforms all the JSON we write into vanilla CSS code.

It is the single source of truth when it comes to managing a block theme’s CSS. And it’s a good idea to know what WordPress is doing behind the scenes when a theme is rendered, so let’s move on to the next article!


Defining Global Styles originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376823
Defining Global Settings https://css-tricks.com/defining-global-settings/ Tue, 31 Jan 2023 15:52:01 +0000 https://css-tricks.com/?page_id=376820 Let's take what we learned about the theme.json structure in WordPress block themes and apply it to two main sections of the file: settings and styles. These arrays are the “top level” for configuring WordPress features and the theme’s CSS output.


Defining Global Settings originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
So far, we’ve spent time getting familiar with theme.json, the foundation for all WordPress block themes. We covered the various JSON data types and common terminology related to working with the JSON syntax.

We are going to build on that knowledge in this article.

Table of contents


What we mean by “top level”

We discussed how JSON has a structured syntax. We can see that in the basic outline of the theme.json file:

{
  "version": 2,
  "settings": {},
  "styles": {},
  "customTemplates": {},
  "templateParts": {}
}

Each object in the basic outline is a “top level” of the file. You can think of the objects as sections where various things are configured. Disregarding the version, there are four top-level sections:

  • settings: This is where we define WordPress presets and defaults; it’s also where we can enable or disable certain WordPress features.
  • styles: This is where we define CSS for global and block-level theme styles.
  • customTemplates: This is where we register new custom theme templates. It’s the equivalent of dropping a new PHP template file in a classic theme.
  • templateParts: This section contains modular pieces that can be included in templates. It’s the equivalent of the parts subfolder you typically see in classic themes.

We’re spending most of our time in the settings and styles sections. Both provide ways to configure the global styles of a WordPress block theme. So, let’s look at each one and the various options we have to customize the theme appearance.

The settings section (settings)

Again, this is where we configure WordPress features, sort of like the add_theme_support function you’d reach for in a classic theme’s functions.php file.

{
  "version": 2,
  "settings": {
    // etc.
  }
}

Why is this relevant to a theme’s global styles? Several WordPress features affect styling, like UI controls for padding, margins, colors, and fonts. Without enabling those, there’s no way to apply these and other styles in the Global Styles UI in the Site Editor.

In short: what we define here is what WordPress uses to add theme support for the Site Editor’s global style settings that are used throughout the theme.

Supported features

The WordPress Block Editor Handbook provides a table that outlines available features we can enable in the settings section and compares them to their equivalent add_theme_support function arguments.

Theme JSON settingsadd_theme_support
color.customcustom-colors
color.paletteeditor-color-palette
color.gradientseditor-gradient-presets
spacing.paddingcustom-spacing
spacing.unitscustom-units
typography.lineHeightcustom-line-height
typography.fontSizeseditor-font-sizes
useRootPaddingAwareAlignmentsN/A

For example, you were using add_theme_support in your classic theme to disable custom font sizes:

<?php
add_theme_support(disable-custom-font-sizes);

The equivalent in theme.json is the settings.typography.customFontSize property:

{
  "version": 2,
  "settings": {
    "typography": {
      "customFontSize": false;
    }
  }
}

As you might imagine, there are nested features within these features. For example, if we were adding support for spacing controls, there are a number of spacing-related features we can individually enable:

{
  "version": 2,
  "settings": {
    "spacing": {
      "margin": false,
      "padding": false,
      "blockGap": null,
      "units": [ "px", "em", "rem", "vh", "vw" ]
    },
  }
}

As of WordPress 6.1, these are the settings features that can be configured in theme.json:

View full table
SettingFeatureData type
borderradiusBoolean
colorBoolean
styleBoolean
widthBoolean
colorcustomBoolean
customDuotoneBoolean
customGradientBoolean
duotoneArray
gradientsArray
linkBoolean
paletteArray
textBoolean
backgroundBoolean
defaultGradientsBoolean
defaultPaletteBoolean
layoutcontentSizeString
wideSizeString
typeString (default and constrained)
spacingmarginBoolean
paddingBoolean
blockGapString
unitsArray
typographycustomFontSizeBoolean
lineHeightBoolean
dropCapBoolean
fontStyleBoolean
fontWeightBoolean
letterSpacingBoolean
textDecorationBoolean
textTransformBoolean
fontSizesArray
fontFamiliesArray

There are ongoing discussions to add the outline and border properties to the Global Styles panel. They are included in this table before formal adoption.

The appearanceTools shorthand

I know that the table shows a ton of features. Configuring each setting could get cumbersome. That’s where the appearanceTools setting comes into play.

{
  "version": 2,
  "settings": {
    "appearanceTools": true
  }
}

Setting it to true enables the following settings in one fell swoop:

{
  "version": 2,
  "settings": {
    "border": {
      "color": true,
      "radius": true,
      "style": true,
      "width": true
    },
    "color": {
      "link": true
    }
    "spacing": {
      "margin": true,
      "padding": true,
      "blockGap": true,
    },
    "typography": {
      "lineHeight": true
    }
  }
}

You can see how many lines of code we can save this way! But let’s say you want to take advantage of appearanceTools, but disable one or two of its supported features. You can do that on an individual basis to override that particular setting:

{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "border": {
      "radius": false
    }
  }
}

The following screenshot shows how setting appearanceTools to true exposes those controls in the Global Styles UI, taken from the default Twenty Twenty-Three theme.

The WordPress Site Editor.

This Learn WordPress video by Nick Diego explains the appearanceTools properties and the Global Styles interface in great detail.

Padding-aware alignments

This feature is worth calling out because it shipped more recently than many other features as part of WordPress 6.1. It is another shortcut for setting two spacing features.

So, this:

{
  "version": 2,
  "settings": {
    "useRootPaddingAwareAlignments": true
  }
}

…is the equivalent of this:

{
  "version": 2,
  "settings": {
    "spacing": {
      "margin": true,
      "padding": true
    },
  }
}

The real benefit of useRootPaddingAwareAlignments is in the name. By enabling it, we opt into a feature that defines global padding on the <body> of our theme and allows blocks to “break out” of that padding and go full-width if we need a block to span edge-to-edge.

See my previous article for a detailed overview of padding-aware alignments.

Configuring preset values

Presets are predefined values used by Global Styles features. More specifically, they define CSS custom properties used throughout a block theme.

So, let’s say you are defining your block theme’s default color palette and add black (#000000) to it:

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        {
          "color": "#000000",
          "name": "Base",
          "slug": "base"
        }
      ]
    }
  }
}

Behind the scenes, something called the Style Engine takes those values and generates the theme’s CSS classes and custom properties. I described the Style Engine in a previous article:

The Style Engine is a new API in WordPress 6.1 that is meant to bring consistency to how styles are generated and where styles are applied. In other words, it takes all of the possible sources of styling and is singularly responsible for properly generating block styles. […] Having a centralized API for styles is probably the most elegant solution given that styles can come from a number of places.

When the Style Engine notices our styles, it creates a set of preset custom properties. For example, that color palette example above? It produces a CSS custom property based on the information we supply it.

If we were to open up DevTools and inspect the <body> element, we would see our custom properties there:

DevTools window showing CSS custom variables on the body element.

Notice how the custom property names are formatted:

--wp-preset--<feature>--<slug>: <value>

So, going back to our original example where we defined black in a color palette:

"color": {
  "palette": [
    {
      "color": "#000000",
      "name": "Base",
      "slug": "Base"
    }
  ]
}

We can expect to find this custom property applied to the <body> element:

body {
  --wp--preset--color--base: #000000;
}

All we’ve looked at are settings.color presets, but nearly every value we define in the settings section results in a custom property:

{
  "version": 2,
  "settings": {
    "spacing": {
      "spacingSizes": [
        {
          // Creates: --wp-preset--spacing--40: 1rem
          "slug": "40",
          "size": "1rem",
          "name": "Small"
        },
        // etc.
      ]
    }
  }
}

Global presets

Now that we know how to define preset values in theme.json, you might want to know what presets are available. The following table outlines what is currently available as of this writing and what they produce.

SettingFeatureCSS custom propertyCSS class
colorduotoneN/AN/A
gradients--wp-preset--gradient--<preset-slug>.has-<preset-slug>-<preset-category>
palette--wp--preset--palette--<preset-slug>Three classes per value:

.has-<preset-value>-color

.has-<preset-value>-background-color

.has-<preset-value>-border-color
spacingspacingScaleN/AN/A
spacingSizes--wp--preset--spacing--<preset-slug>.has-<preset-slug>-spacing
typographyfontSizes--wp--preset--font-size--<preset-slug>.has-<preset-slug>-font-size
fontFamilies--wp--preset--font-family--<preset-slug>.has-<preset-slug>-font-family

Block presets

We can get more granular when defining preset values in theme.json. In addition to the global presets we just saw, we can define presents at the block level.

Let’s say we have the following global color presets:

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        {
          // --wp-preset--color--base: #ffffff
          "color": "#FFFFFF",
          "name": "Base",
          "slug": "base"
        },
        {
          // --wp-preset--color--contrast: #000000
          "color": "#000000",
          "name": "Contrast",
          "slug": "contrast"
        }
      ]
    }
  }
}

The custom properties generated by those settings are used throughout the theme, including blocks. So, if we were to add a Separator block to a page in the Block Editor, it will refer to the same color palette when it renders.

But we can override that palette — or any of the others in the global settings — with a palette specifically for the Separator block:

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [ // etc. ]
    },
    "blocks": {
      "separator": {
        "color": {
          "palette": [
            {
              "color": "#F8A100",
              "name": "Orange",
              "slug": "orange"
            }
          ]
        }
      }
    }
  }
}

In this example, the Separator block’s palette overrides the global palette with a single orange (#F8A100) color value.

Custom presets

You can also create “custom” CSS custom properties on any property. In addition to the settings features we’ve looked at so far, there is a custom property where we can do that.

{
  "version": 2,
  "settings": {
    "custom": {
      // etc.
    }
  }
}

The Style Engine takes anything we define in there and generates CSS custom properties from it. The naming convention is pretty similar to the custom properties generated by the other settings:

--wp--custom--<variable-name>: <value>

Your defined values in custom will be transformed into CSS custom properties and use the --wp--custom--<variable-name> naming convention.

Here’s an abbreviated example of custom settings pulled straight from the default Twenty Twenty-Two theme. In it are custom settings for typography.font-size and typography.line-height:

{
  "version": 2,
  "settings": {
    "custom": {
      "typography": {
        "font-size": {
          "huge": "clamp(2.25rem, 4vw, 2.75rem)",
          "gigantic": "clamp(2.75rem, 6vw, 3.25rem)",
          "colossal": "clamp(3.25rem, 8vw, 6.25rem)"
        },
        "line-height": {
          "tiny": 1.15,
          "small": 1.2,
          "medium": 1.4,
          "normal": 1.6
        }
      }
    }
  }
}

Wrapping up

We learned a great deal about the settings section of the theme.json file. We covered the available settings and how they are used to define global styles that are used in the Global Styles UI of the WordPress Site Editor. From there, we learned about preset values, including which ones we can configure and how they are generated into CSS custom properties by the Style Engine.

What we haven’t covered is how we can use theme.json to define CSS styles in a block theme. That happens in another top-level section called styles, which is the focus of the next part of this series.


Defining Global Settings originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376820
Glossary of Terms for theme.json https://css-tricks.com/glossary-of-terms-for-theme-json/ Tue, 31 Jan 2023 15:45:02 +0000 https://css-tricks.com/?page_id=376818 To kick things off, let’s begin by reviewing a few glossary terms that are important for understanding what the theme.json file is, how it is structured, and how to configure it. We’ll cover examples as we go, but the main goal here is to get familiar with terms that we’ll be seeing throughout this series.


Glossary of Terms for theme.json originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Common JSON terms

We’re going to spend a quick moment getting familiar with JSON-related terms before jumping to the next section that outlines the six different types of JSON data types.

JSON

JSON refers to JavaScript Object Notation, a machine-readable data-sharing format. As the name suggests, JSON is derived from JavaScript and applied to many other languages like PHP, Ruby, Python, etc. Many software applications, including React, Gatsby, VS Code, and others, use JSON for settings environments.

Object

Objects are key-value pairs separated by colons : and contained inside curly brackets ({}). You can think of it like CSS in a way. We have a property (or key) followed by a value that defines the property:

color: #9DFF20;

In JSON, that is represented as an object like this:

{ "color": "#9DFF20" }

Note: A JSON object property is also called a field or key. A key-value pair is also called an item or data.

And like a CSS ruleset can contain many key-value pairs, so can a JSON object. This example is an object with three keys — color, name, and slug — each with a corresponding value:

{ "color": "#9DFF20", "name": "Primary", "slug": "primary" }

Contrary to CSS, JSON is not a “forgiving” language, and even one typing error can break the entire website.

Nested object

Objects in JSON can contain other objects. We call these nested objects, and it’s what makes JSON a structured language.

{
  "object1": {
    "object1a": "value",
    "object1b": "value"
  },
  "object2" : {
    "object2a": "value",
    "object2b": "value"
  }
}

Let’s take an example straight from the emptytheme theme.json file:

Array

An array is a comma-separated group of objects in square brackets ([]). The idea is that we might need to define more than one object at a time. A good example is defining the colors of a block theme’s color palette. An array allows us to define a group of colors for that palette:

JSON data types

JSON values must be one of the six data types: a string, a number, an object, an array, a boolean (true or false), and null. JSON data are assessed using dot . notation.

Here is an abbreviated modified TT3 theme.json object file showing all the data types:

{
  "version": 2, // 1: Number (no quotes)
  "settings": {
    "appearanceTools": true, // 2: Boolean (true or false, no quotes)
    "useRootPaddingAwareAlignments": false,
    "color": {
      "palette": [ // 3: Array of string object palette
        { 
          "color": "#ffffff", // 4: Object (in curly brackets)
          "name": "Base",
          "slug": "base"
        }
      ]
    },
    "layout": { "contentSize": "650px"}, // 5: String (in double quotes)
    "styles": {
      "typography": { "lineHeight": "1.6" },
      "spacing": { "blockGap": null } // 6: null (no quotes)
    }
  }
}

Additional resources

Next up…

Now that we have a solid understanding of JSON and how it is structured in the theme.json of WordPress block themes, let’s take a closer look at how global styling works. We can define default styles for a block theme directly in theme.json. We can also use the Global Styles UI in WordPress.

How do those work together? What sort of settings are available? When should you use one over the other? We’ll answer those questions and more next.


Glossary of Terms for theme.json originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376818
WordPress Block Themes CSS and Style Settings Guide https://css-tricks.com/wordpress-block-theme-guide/ Tue, 31 Jan 2023 15:43:43 +0000 https://css-tricks.com/?page_id=376813 Managing CSS in WordPress has dramatically changed since full-site editing features were introduced to block themes. This guide is geared toward block themes and how to configure them, from enabling editor features and controls to defining theme-wide CSS and customizaing the appearance of specific blocks.


WordPress Block Themes CSS and Style Settings Guide originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In the 2015 State of the World address, WordPress co-creator Matt Mullenweg famously directed the WordPress community to“Learn JavaScript deeply”. He set that expectation upfront because he wanted everyone to know that JavaScript would be essential to the future of WordPress, which was paving a path toward full-site editing with a React-based editing experience to be called the Block Editor.

That was years ago, and a concerted effort has been taking place to shift WordPress theme development from “classic” PHP-based templating to a more modular, component-driven model centered on “blocks” for constructing page and post layouts.

Now that we’re starting to see new block-based WordPress themes hit the Theme Directory, many of us who have been developing WordPress themes for years using “classic” PHP templates we’re sitting in some sort of middle-ground between “classic” and “block” themes. Geoff expanded on this feeling in another post, but the general sentiment is that working in WordPress is much different than before.

Where do you even start on a WordPress Block Theme project? That’s what this guide is all about. We could get into the nuances of working with React, but there’s already a good guide for that here on CSS-Tricks as well as tutorials on working with blocks.

Instead, this guide is geared toward block themes and how to configure them. Think of it as an extension to my previous article on managing styles in WordPress block themes. In there, we covered how to define CSS styles in the theme.json file — the foundation of all WordPress block themes, akin to how style.css is used in classic themes. We’re going to go deeper in this series, giving theme.json a proper introduction and documenting how it’s used to manage the appearance of a WordPress site that fully supports full-site editing features.

As it currently stands, finding resources and proper documentation for defining and managing styles in WordPress block themes is a task in and of itself. Unless you keep up with GitHub issues, Gutenberg plugin releases, and Make WordPress Core, you could feel lost in this new WordPress landscape. The WordPress Handbook won’t save you either because it is constantly several steps behind the breakneck speed of development.

So, let’s pull all of that together and learn about managing styles in WordPress block themes.


WordPress Block Themes CSS and Style Settings Guide originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
376813
Styling Buttons in WordPress Block Themes https://css-tricks.com/styling-buttons-in-wordpress-block-themes/ https://css-tricks.com/styling-buttons-in-wordpress-block-themes/#comments Mon, 09 Jan 2023 13:56:19 +0000 https://css-tricks.com/?p=376237 A little while back, Ganesh Dahal penned a post here on CSS-Tricks responding to a tweet that asked about adding CSS box shadows on WordPress blocks and elements. There’s a lot of great stuff in there that leverages new features …


Styling Buttons in WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
A little while back, Ganesh Dahal penned a post here on CSS-Tricks responding to a tweet that asked about adding CSS box shadows on WordPress blocks and elements. There’s a lot of great stuff in there that leverages new features that shipped in WordPress 6.1 that provide controls for applying shadows to things directly in the Block Editor and Site Editor UI.

Ganesh touched briefly on button elements in that post. I want to pick that up and go deeper into approaches for styling buttons in WordPress block themes. Specifically, we’re going to crack open a fresh theme.json file and break down various approaches to styling buttons in the schema.

Why buttons, you ask? That’s a good question, so let’s start with that.

The different types of buttons

When we’re talking about buttons in the context of the WordPress Block Editor, we have to distinguish between two different types:

  1. Child blocks inside of the Buttons block
  2. Buttons that are nested inside other block (e.g. the Post Comments Form block)

If we add both of these blocks to a template, they have the same look by default.

A black button above a comment form that also contains a black button.

But the markup is very different:

<div class="wp-block-button">
  <a class="wp-block-button__link wp-element-button">Button 1</a>
</div>
<p class="form-submit wp-block-button">
  <input name="submit" type="submit" id="submit" class="wp-block-button__link wp-element-button" value="Post Comment"> 
</p>

As we can see, the HTML tag names are different. It’s the common classes — .wp-block-button and .wp-element-button — that ensure consistent styling between the two buttons.

If we were writing CSS, we would target these two classes. But as we know, WordPress block themes have a different way of managing styles, and that’s through the theme.json file. Ganesh also covered this in great detail, and you’d do well giving his article a read.

So, how do we define button styles in theme.json without writing actual CSS? Let’s do it together.

Creating the base styles

theme.json is a structured set of schema written in property:value pairs. The top level properties are called “sections”, and we’re going to work with the styles section. This is where all the styling instructions go.

We’ll focus specifically on the elements in the styles. This selector targets HTML elements that are shared between blocks. This is the basic shell we’re working with:

// theme.json
{
  "version": 2,
  "styles": {
    "elements": {
      // etc.
    }
  }
}

So what we need to do is define a button element.

={
  "version": 2,
  "styles": {
    "elements": {
      "button": {
        // etc.
      }
    }
  }
}

That button corresponds to HTML elements that are used to mark up button elements on the front end. These buttons contain HTML tags that could be either of our two button types: a standalone component (i.e. the Button block) or a component nested within another block (e.g. the Post Comment block).

Rather than having to style each individual block, we create shared styles. Let’s go ahead and change the default background and text color for both types of buttons in our theme. There’s a color object in there that, in turn, supports background and text properties where we set the values we want:

{
  "version": 2,
  "styles": {
    "elements": {
      "button": {
        "color": {
          "background": "#17a2b8",
          "text": "#ffffff"
        }
      }
    }
  }
}

This changes the color of both button types:

A light blue button above a comment form that also contains a light blue button.

If crack open DevTools and have a look at the CSS that WordPress generates for the buttons, we see that the .wp-element-button class adds the styles we defined in theme.json:

.wp-element-button {
  background-color: #17a2b8;
  color: #ffffff;
}

Those are our default colors! Next, we want to give users visual feedback when they interact with the button.

Implementing interactive button styles

Since this is a site all about CSS, I’d bet many of you are already familiar with the interactive states of links and buttons. We can :hover the mouse cursor over them, tab them into :focus, click on them to make them :active. Heck, there’s even a :visited state to give users a visual indication that they’ve clicked this before.

Those are CSS pseudo-classes and we use them to target a link’s or button’s interactions.

In CSS, we might style a :hover state like this:

a:hover {
  /* Styles */
}

In theme.json, we’re going to extend our existing button declaration with these pseudo-classes.

{
  "version": 2,
  "styles": {
    "elements": {
      "button": {
        "color": {
          "background": "#17a2b8",
          "text": "#ffffff"
        },
        ":hover": {
          "color": {
            "background": "#138496"
          }
        },
        ":focus": {
          "color": {
            "background": "#138496"
          }
        },
        ":active": {
          "color": {
            "background": "#138496"
          }
        }
      }
    }
  }
}

Notice the “structured” nature of this. We’re basically following an outline:

  • Elements
    • Element
      • Object
        • Property
          • Value

We now have a complete definition of our button’s default and interactive styles. But what if we want to style certain buttons that are nested in other blocks?

Styling buttons nested in individual blocks

Let’s imagine that we want all buttons to have our base styles, with one exception. We want the submit button of the Post Comment Form block to be blue. How would we achieve that?

This block is more complex than the Button block because it has more moving parts: the form, inputs, instructive text, and the button. In order to target the button in this block, we have to follow the same sort of JSON structure we did for the button element, but applied to the Post Comment Form block, which is mapped to the core/post-comments-form element:

{
  "version": 2,
  "styles": {
    "elements" {
      "button": {
        // Default button styles
      }
    }
    "blocks": {
      "core/post-comments-form": {
        // etc.
      }
    }
  }
}

Notice that we’re no longer working in elements anymore. Instead, we’re working inside blocks which is reserved for configuring actual blocks. Buttons, by contrast, are considered a global element since they can be nested in blocks, even though they are available as a standalone block too.

The JSON structure supports elements within elements. So, if there’s a button element in the Post Comment Form block, we can target it in the core/post-comments-form block:

{
  "version": 2,
  "styles": {
    "elements" {
      "button": {
        // Default button styles
      }
    }
    "blocks": {
      "core/post-comments-form": {
        "elements": {
          "button": {
            "color": {
              "background": "#007bff"
            }
          }
        }
      }
    }
  }
}

This selector means that not only are we targeting a specific block — we’re targeting a specific element that is contained in that block. Now we have a default set of button styles that are applied to all buttons in the theme, and a set of styles that apply to specific buttons that are contained in the Post Comment Form block.

A light blue button above a comment form that contans a bright blue button.

The CSS generated by WordPress has a more precise selector as a result:

.wp-block-post-comments-form .wp-element-button,
.wp-block-post-comments-form .wp-block-button__link {
  background-color: #007bff;
}

And what if we want to define different interactive styles for the Post Comment Form button? It’s the same deal as the way we did it for the default styles, only those are defined inside the core/post-comments-form block:

{
  "version": 2,
  "styles": {
    "elements" {
      "button": {
        // Default button styles
      }
    }
    "blocks": {
      "core/post-comments-form": {
        "elements": {
          "button": {
            "color": {
              "background": "#007bff"
            }
            ":hover": {
              "color": {
                "background": "#138496"
              }
            },
            // etc.
          }
        }
      }
    }
  }
}

What about buttons that are not in blocks?

WordPress automagically generates and applies the right classes to output these button styles. But what if you use a “hybrid” WordPress theme that supports blocks and full-site editing, but also contains “classic” PHP templates? Or what if you made a custom block, or even have a legacy shortcode, that contains buttons? None of these are handled by the WordPress Style Engine!

No worries. In all of those cases, you would add the .wp-element-button class in the template, block, or shortcode markup. The styles generated by WordPress will then be applied in those instances.

And there may be some situations where you have no control over the markup. For example, some block plugin might be a little too opinionated and liberally apply its own styling. That’s where you can typically go to the “Advanced” option in the block’s settings panel and apply the class there:

A WordPress block settings panel with the Advanced settings expanded highlighting the CSS classes section in red.

Wrapping up

While writing “CSS” in theme.json might feel awkward at first, I’ve found that it becomes second nature. Like CSS, there are a limited number of properties that you can apply either broadly or very narrowly using the right selectors.

And let’s not forget the three main advantages of using theme.json:

  1. The styles are applied to buttons in both the front-end view and the block editor.
  2. Your CSS will be compatible with future WordPress updates.
  3. The generated styles work with block themes and classic themes alike — there’s no need to duplicate anything in a separate stylesheet.

If you have used theme.json styles in your projects, please share your experiences and thoughts. I look forward to reading any comments and feedback!


Styling Buttons in WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/styling-buttons-in-wordpress-block-themes/feed/ 2 376237
Saving Settings for a Custom WordPress Block in the Block Editor https://css-tricks.com/saving-settings-for-a-custom-wordpress-block-in-the-block-editor/ https://css-tricks.com/saving-settings-for-a-custom-wordpress-block-in-the-block-editor/#respond Mon, 12 Dec 2022 14:06:39 +0000 https://css-tricks.com/?p=375577 We’ve accomplished a bunch of stuff in this series! We created a custom WordPress block that fetches data from an external API and renders it on the front end. Then we took that work and extended it so the data …


Saving Settings for a Custom WordPress Block in the Block Editor originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’ve accomplished a bunch of stuff in this series! We created a custom WordPress block that fetches data from an external API and renders it on the front end. Then we took that work and extended it so the data also renders directly in the WordPress block editor. After that, we created a settings UI for the block using components from the WordPress InspectorControls package.

There’s one last bit for us to cover and that’s saving the settings options. If we recall from the last article, we’re technically able to “save” our selections in the block settings UI, but those aren’t actually stored anywhere. If we make a few selections, save them, then return to the post, the settings are completely reset.

Let’s close the loop and save those settings so they persist the next time we edit a post that contains our custom block!

Working With External APIs in WordPress Blocks

Saving settings attributes

We’re working with an API that provides us with soccer football team ranking and we’re using it to fetch for displaying rankings based on country, league, and season. We can create new attributes for each of those like this:

// index.js

attributes: {
  data: {
    type: "object",
  },
  settings: {
    type: "object",
    default: {
      country: {
        type: "string",
      },
      league: {
        type: "string",
      },
      season: {
        type: "string",
      },
    },
  },
},

Next, we need to set the attributes from LeagueSettings.js. Whenever a ComboboxControl is updated in our settings UI, we need to set the attributes using the setAttributes() method. This was more straightfoward when we were only working with one data endpoint. But now that we have multiple inputs, it’s a little more involved.

This is how I am going to organize it. I am going to create a new object in LeagueSettings.js that follows the structure of the settings attributes and their values.

// LeagueSettings.js

let localSettings = {
  country: attributes.settings.country,
  league: attributes.settings.league,
  season: attributes.settings.season,
};

I am also going to change the initial state variables from null to the respective settings variables.

// LeagueSettings.js

const [country, setCountry] = useState(attributes.settings.country);
const [league, setLeague] = useState(attributes.settings.league);
const [season, setSeason] = useState(attributes.settings.season);

In each of the handle______Change(), I am going to create a setLocalAttributes() that has an argument that clones and overwrites the previous localSettings object with the new country, league, and season values. This is done using the help of the spread operator.

// LeagueSettings.js

function handleCountryChange(value) {
  // Initial code
  setLocalAttributes({ ...localSettings, country: value });
  // Rest of the code
}

function handleLeagueChange(value) {
  // Initial code
  setLocalAttributes({ ...localSettings, league: value });
  // Rest of the code
}

function handleSeasonChange(value) {
  // Initial code
  setLocalAttributes({ ...localSettings, season: value });
  // Rest of the code
}

We can define the setLocalAttributes() like this:

// LeagueSettings.js

function setLocalAttributes(value) {
  let newSettings = Object.assign(localSettings, value);
  localSettings = { ...newSettings };
  setAttributes({ settings: localSettings });
}

So, we’re using Object.assign() to merge the two objects. Then we can clone the newSettings object back to localSettings because we also need to account for each settings attribute when there a new selection is made and a change occurs.

Finally, we can use the setAttributes() as we do normally to set the final object. You can confirm if the above attributes are changing by updating the selections in the UI.

Another way to confirm is to do a console.log() in DevTools to find the attributes.

The block added to a post in the block editor with DevTools open showing the saved attributes.

Look closer at that screenshot. The values are stored in attributes.settings. We are able to see it happen live because React re-renders every time we make a change in the settings, thanks to the useState() hook.

Displaying the values in the blocks settings UI

It isn’t very useful to store the setting values in the control options themselves since each one is dependent on the other setting value (e.g. rankings by league depends on which season is selected). But it is very useful in situations where the settings values are static and where settings are independent of each other.

Without making the current settings complicated, we can create another section inside the settings panel that shows the current attributes. You can choose your way to display the settings values but I am going to import a Tip component from the @wordpress/components package:

// LeagueSettings.js
import { Tip } from "@wordpress/components";

While I’m here, I am going to do a conditional check for the values before displaying them inside the Tip component:

<Tip>
  {country && league && season && (
    <>
      <h2>Current Settings: </h2>
      <div className="current-settings">
        <div className="country">
          Country: {attributes.settings.country}
        </div>
        <div className="league">
          League: {attributes.settings.league}
        </div>
        <div className="season">
          Season: {attributes.settings.season}
        </div>
      </div>
    </>
  )}
</Tip>

Here’s how that winds up working in the block editor:

API data is more powerful when live data can be shown without having to manually update them each and every time. We will look into that in the next installment of this series.


Saving Settings for a Custom WordPress Block in the Block Editor originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/saving-settings-for-a-custom-wordpress-block-in-the-block-editor/feed/ 0 375577
Adding Box Shadows to WordPress Blocks and Elements https://css-tricks.com/adding-box-shadows-to-wordpress-blocks-and-elements/ https://css-tricks.com/adding-box-shadows-to-wordpress-blocks-and-elements/#respond Wed, 07 Dec 2022 13:59:50 +0000 https://css-tricks.com/?p=375412 The CSS box-shadow and outline properties gained theme.json support in WordPress 6.1. Let's look at a few examples of how it works in real themes, and what options we have to apply these styles to WordPress blocks and elements.


Adding Box Shadows to WordPress Blocks and Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I stumbled across this tweet from Ana Segota looking for a way to add a CSS box-shadow to a button’s hover state in WordPress in the theme.json file.

She’s asking because theme.json is where WordPress wants us to start moving basic styles for block themes. Traditionally, we’d do any and all styling in style.css when working in a “classic” theme. But with the default Twenty Twenty-Three (TT3) theme that recently shipped with WordPress 6.1 moving all of its styles to theme.json, we’re getting closer and closer to being able to do the same with our own themes. I covered this in great detail in a recent article.

I say “closer and closer” because there are still plenty of CSS properties and selectors that are unsupported in theme.json. For example, if you’re hoping to style something with like perspective-origin in theme.json, it just won’t happen — at least as I’m writing this today.

Ana is looking at box-shadow and, luckily for her, that CSS property is supported by theme.json as of WordPress 6.1. Her tweet is dated Nov. 1, the same exact day that 6.1 released. It’s not like support for the property was a headline feature in the release. The bigger headlines were more related to spacing and layout techniques for blocks and block themes.

Here’s how we can apply a box-shadow to a specific block — say the Featured Image block — in theme.json:

{
  "version": 2,
  "settings": {},
  // etc.
  "styles": {
    "blocks" :{
      "core/post-featured-image": {
        "shadow": "10px 10px 5px 0px rgba(0, 0, 0, 0.66)"
      }
    }
  }
}

Wondering if the new color syntax works? Me too! But when I tried — rgb(0 0 0 / 0.66) — I got nothing. Perhaps that’s already in the works or could use a pull request.

Easy, right? Sure, it’s way different than writing vanilla CSS in style.css and takes some getting used to. But it is indeed possible as of the most recent WordPress release.

And, hey, we can do the same thing to individual “elements”, like a button. A button is a block in and of itself, but it can also be a nested block within another block. So, to apply a box-shadow globally to all buttons, we’d do something like this in theme.json:

{
  "version": 2,
  "settings": {},
  // etc.
  "styles": {
    "elements": {
      "button": {
        "shadow": "10px 10px 5px 0px rgba(0,0,0,0.66)"
      }
    }
  }
}

But Ana wants to add the shadow to the button’s :hover state. Thankfully, support for styling interactive states for certain elements, like buttons and links, using pseudo-classes — including :hover, :focus, :active, and :visited — also gained theme.json support in WordPress 6.1.

{
  "version": 2,
  "settings": {},
  // etc.
  "styles": {
    "elements": {
      "button": {
        ":hover": {
          "shadow": "10px 10px 5px 0px rgba(0,0,0,0.66)"
        }
      }
    }
  }
}

If you’re using a parent theme, you can certainly override a theme’s styles in a child theme. Here, I am completely overriding TT3’s button styles.

View full code
{
  "version": 2,
  "settings": {},
  // etc.
  "styles": {
    "elements": {
      "button": {
        "border": {
          "radius": "0"
        },
        "color": {
          "background": "var(--wp--preset--color--tertiary)",
          "text": "var(--wp--preset--color--contrast)"
        },
        "outline": {
          "offset": "3px",
          "width": "3px",
          "style": "dashed",
          "color": "red"
        },
        "typography": {
          "fontSize": "var(--wp--preset--font-size--medium)"
        },
        "shadow": "5px 5px 5px 0px rgba(9, 30, 66, 0.25), 5px 5px 5px 1px rgba(9, 30, 66, 0.08)",
        ":hover": {
          "color": {
            "background": "var(--wp--preset--color--contrast)",
            "text": "var(--wp--preset--color--base)"
          },
          "outline": {
            "offset": "3px",
            "width": "3px",
            "style": "solid",
            "color": "blue"
          }
        },
        ":focus": {
          "color": {
            "background": "var(--wp--preset--color--contrast)",
            "text": "var(--wp--preset--color--base)"
          }
        },
        ":active": {
          "color": {
            "background": "var(--wp--preset--color--secondary)",
            "text": "var(--wp--preset--color--base)"
          }
        }
      }
    }
  }
}

Here’s how that renders:

Showing two red buttons with box shadows.
The button’s natural state (left) and it’s hovered state (right)

Another way to do it: custom styles

The recently released Pixl block theme provides another example of real-world usage of the box-shadow property in theme.json using an alternative method that defines custom values. In the theme, a custom box-shadow property is defined as .settings.custom.shadow:

{
  "version": 2,
  "settings": {
    // etc. 
    "custom": {
      // etc.
      "shadow": "5px 5px 0px -2px var(--wp--preset--color--background), 5px 5px var(--wp--preset--color--foreground)"
    },
    // etc.
  }
}

Then, later in the file, the custom shadow property is called on a button element:

{
  "version": 2,
  "settings": {
    // etc.
  },
  "styles": {
    "elements": {
      "button": {
        // etc.
        "shadow": "var(--wp--custom--shadow) !important",
        // etc.
        ":active": {
          // etc.
          "shadow": "2px 2px var(--wp--preset--color--primary) !important"
        }
      },
    // etc.
  }
}

I’m not totally sure about the use of !important in this context. My hunch is that it’s an attempt to prevent overriding those styles using the Global Styles UI in the Site Editor, which has high specificity than styles defined in theme.json. Here’s an anchored link to more information from my previous article on managing block theme styles.

Update: Turns out there was a whole discussion about this in Pull Request #34689, which notes that it was addressed in WordPress 5.9.

And there’s more…

In addition to shadows, the CSS outline property also gained theme.json support in WordPress 6.1 and can be applied to buttons and their interactive states. This GitHub PR shows a good example.

"elements": {
  "button": {
    "outline": {
      "offset": "3px",
      "width": "3px",
      "style": "dashed",
      "color": "red"
    },
    ":hover": {
      "outline": {
        "offset": "3px",
        "width": "3px",
        "style": "solid",
        "color": "blue"
      }
    }
  }
}

You can also find the real examples of how the outline property works in other themes, including Loudness, Block Canvas, and Blockbase.

Wrapping up

Who knew there was so much to talk about with a single CSS property when it comes to block theming in WordPress 6.1? We saw the officially supported methods for setting a box-shadow on blocks and individual elements, including the interactive states of a button element. We also checked out how we could override shadows in a child theme. And, finally, we cracked open a real-world example that defines and sets shadows in a custom property.

You can find more detailed in-depth discussions about the WordPress and it’s box-shadow implementation in this GitHub PR. There is also a GitHub proposal for adding UI directly in WordPress to set shadow values on blocks — you can jump directly to an animated GIF showing how that would work.

Speaking of which, Justin Tadlock recently developed a block that renders a progress bar and integrated box shadow controls into it. He shows it off in this video:

More information

If you’d like to dig deeper into the box-shadow and other CSS properties that are supported by the theme.json file in a block theme, here are a couple of resources you can use:


Adding Box Shadows to WordPress Blocks and Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/adding-box-shadows-to-wordpress-blocks-and-elements/feed/ 0 https://www.youtube.com/embed/TMd3NHBPZ-8 Box Shadow Control with the Progress Bar WordPress Block nonadult 375412
Creating a Settings UI for a Custom WordPress Block https://css-tricks.com/creating-a-settings-ui-for-a-custom-wordpress-block/ https://css-tricks.com/creating-a-settings-ui-for-a-custom-wordpress-block/#comments Thu, 17 Nov 2022 13:48:26 +0000 https://css-tricks.com/?p=375098 So far, we’ve covered how to work with data from an external API in a custom WordPress block. We walked through the process of fetching that data for use on the front end of a WordPress site, and how to …


Creating a Settings UI for a Custom WordPress Block originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
So far, we’ve covered how to work with data from an external API in a custom WordPress block. We walked through the process of fetching that data for use on the front end of a WordPress site, and how to render it directly in the WordPress Block Editor when placing the block in content. This time, we’re going to bridge those two articles by hooking into the block editor’s control panel to create a settings UI for the block we made.

Working With External APIs in WordPress Blocks

You know the control panel I’m referring to, right? It’s that panel on the right that contains post and block settings in the block editor.

WordPress block editor with the right control panel open containing the settings UI for a Paragraph block.

See that red highlighted area? That’s the control panel. A Paragraph block is currently selected and the settings for it are displayed in the panel. We can change styles, color, typography… a number of things!

Well, that’s exactly what we’re doing this time around. We’re going to create the controls for the settings of the Football Rankings block we worked on in the last two articles. Last time, we made a button in our block that fetches the external data for the football rankings. We already knew the URL and endpoints we needed. But what if we want to fetch ranking for a different country? Or maybe a different league? How about data from a different season?

We need form controls to do that. We could make use of interactive React components — like React-Select — to browse through the various API options that are available to parse that data. But there’s no need for that since WordPress ships with a bunch of core components that we hook right into!

The documentation for these components — called InspectorControls — is getting better in the WordPress Block Editor Handbook. That’ll get even better over time, but meanwhile, we also have the WordPress Gutenberg Storybook and WordPress Gutenberg Components sites for additional help.

The API architecture

Before we hook into anything, it’s a good idea to map out what it is we need in the first place. I’ve mapped out the structure of the RapidAPI data we’re fetching so we know what’s available to us:

Flow chart connecting the API endpoints for the custom WordPress block data that is fetched.
Credit: API-Football

Seasons and countries are two top-level endpoints that map to a leagues endpoint. From there, we have the rest of the data we’re already using to populate the rankings table. So, what we want to do is create settings in the WordPress Block Editor that filter the data by Season, Country, and League, then pass that filtered data into the rankings table. That gives us the ability to drop the block in any WordPress page or post and display variations of the data in the block.

In order to get the standings, we need to first get the leagues. And in order to get the leagues, we first need to get the countries and/or the seasons. You can view the various endpoints in the RapidAPI dashboard.

Full screen for the Rapid API dashboard that visualizes the API data.

There are different combinations of data that we can use to populate the rankings, and you might have a preference for which data you want. For the sake of this article, we are going to create the following options in the block settings panel:

  • Choose Country
  • Choose League
  • Choose Season

Then we’ll have a button to submit those selections and fetch the relevant data and pass them into the rankings table.

Load and store a list of countries

We can’t select which country we want data for if we don’t have a list of countries to choose from. So, our first task is to grab a list of countries from RapidAPI.

The ideal thing is to fetch the list of countries when the block is actually used in the page or post content. There’s no need to fetch anything if the block isn’t in use. The approach is very similar to what we did in the first article, the difference being that we are using a different API endpoint and different attributes to store the list of returned countries. There are other WordPress ways to fetch data, like api-fetch, but that‘s outside the scope of what we’re doing here.

We can either include the country list manually after copying it from the API data, or we could use a separate API or library to populate the countries. But the API we’re using already has a list of countries, so I would just use one of its endpoints. Let’s make sure the initial country list loads when the block is inserted into the page or post content in the block editor:

// edit.js
const [countriesList, setCountriesList] = useState(null);

useEffect(() => {
  let countryOptions = {
    method: "GET",
    headers: {
      "X-RapidAPI-Key": "Your Rapid API key",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  };
  fetch("https://api-football-v1.p.rapidapi.com/v3/countries", countryOptions)
    .then( (response) => response.json() )
    .then( (response) => {
      let countriesArray = { ...response };
      console.log("Countries list", countriesArray.response);
      setCountriesList(countriesArray.response);
    })
  .catch((err) => console.error(err));
}, []);

We have a state variable to store the list of countries. Next, we are going to import a component from the @wordpress/block-editor package called InspectorControls which is where all of the components we need to create our settings controls are located.

import { InspectorControls } from "@wordpress/block-editor";

The package’s GitHub repo does a good job explaining InspectorControls. In our example, we can use it to control the API data settings like Country, League, and Season. Here’s a preview so that you get an idea of the UI we’re making:

The custom settings UI for the WordPress block showing the three settings options for the custom block and a blue button to fetch the data.

And once those selections are made in the block settings, we use them in the block’s Edit function:

<InspectorControls>
  { countriesList && (
    <LeagueSettings
      props={props}
      countriesList={ countriesList }
      setApiData={ setApiData }
    ></LeagueSettings>
  )}
</InspectorControls>

Here, I am making sure that we are using conditional rendering so that the function only loads the component after the list of countries is loaded. If you’re wondering about that LeagueSettings component, it is a custom component I created in a separate components subfolder in the block so we can have a cleaner and more organized Edit function instead of hundreds of lines of country data to deal with in a single file.

File structure for the block directory showing the current file.

We can import it into the edit.js file like this:

import { LeagueSettings } from "./components/LeagueSettings";

Next, we’re passing the required props to the LeagueSettings component from the parent Edit component so that we can access the state variables and attributes from the LeagueSettings child component. We can also do that with other methods like the Context API to avoid prop drilling, but what we have right now is perfectly suitable for what we’re doing.

The other parts of the Edit function can also be converted into components. For example, the league standings code can be put inside a separate component — like maybe LeagueTable.js — and then imported just like we imported LeagueSettings into the Edit function.

Inside the LeagueSettings.js file

LeagueSettings is just like another React component from which we can destructure the props from the parent component. I am going to use three state variables and an additional leagueID state because we are going to extract the ID from the league object:

const [country, setCountry] = useState(null);
const [league, setLeague] = useState(null);
const [season, setSeason] = useState(null);
const [leagueID, setLeagueID] = useState(null);

The first thing we’re going to do is import the PanelBody component from the @wordpress/block-editor package:

import { PanelBody } from "@wordpress/block-editor";

…and include it in our return function:

<PanelBody title="Data settings" initialOpen={false}></PanelBody>

There are other panel tags and attributes — it’s just my personal preference to use these ones. None of the others are required… but look at all the components we have available to make a settings panel! I like the simplicity of the PanelBody for our use case. It expands and collapses to reveal the dropdown settings for the block and that’s it.

Speaking of which, we have a choice to make for those selections. We could use the SelectControl component or a ComboBoxControl, which the docs describe as “an enhanced version of a SelectControl, with the addition of being able to search for options using a search input.” That’s nice for us because the list of countries could get pretty long and users will be able to either do a search query or select from a list.

Here’s an example of how a ComboboxControl could work for our country list:

<ComboboxControl
  label="Choose country"
  value={country}
  options={ filteredCountryOptions }
  onChange={ (value) => handleCountryChange(value) }
  onInputChange={ (inputValue) => {
    setFilteredCountryOptions(
      setupCountrySelect.filter((option) =>
        option.label
          .toLowerCase()
          .startsWith(inputValue.toLowerCase())
      )
    );
  }}
/>

The ComboboxControl is configurable in the sense that we can apply different sizing for the control’s label and values:

{
  value: 'small',
  label: 'Small',
},

But our API data is not in this syntax, so we can convert the countriesList array that comes from the parent component when the block is included:

let setupCountrySelect;

setupCountrySelect = countriesList.map((country) => {
  return {
    label: country.name,
    value: country.name,
  };
});

When a country is selected from the ComboboxControl, the country value changes and we filter the data accordingly:

function handleCountryChange(value) {
  // Set state of the country 
  setCountry(value); 

  // League code from RapidAPI
  const options = {
    method: "GET",
    headers: {
      "X-RapidAPI-Key": "Your RapidAPI key",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  };

  fetch(`https://api-football-v1.p.rapidapi.com/v3/leagues?country=${value}`, options)
    .then((response) => response.json())
    .then((response) => {
      return response.response;
    })
    .then((leagueOptions) => {
      // Set state of the league variable
      setLeague(leagueOptions);

      // Convert it as we did for Country options
      setupLeagueSelect = leagueOptions.map((league) => {
        return {
          label: league.league.name,
          value: league.league.name,
        };
      });
      setFilteredLeagueOptions(setupLeagueSelect);
    })
  .catch((err) => console.error(err));
}

Note that I am using another three state variables to handle changes when the country selection changes:

const [filteredCountryOptions, setFilteredCountryOptions] = useState(setupCountrySelect);
const [filteredLeagueOptions, setFilteredLeagueOptions] = useState(null);
const [filteredSeasonOptions, setFilteredSeasonOptions] = useState(null);

What about the other settings options?

I will show the code that I used for the other settings but all it does is take normal cases into account while defining errors for special cases. For example, there will be errors in some countries and leagues because:

  • there are no standings for some leagues, and
  • some leagues have standings but they are not in a single table.

This isn’t a JavaScript or React tutorial, so I will let you handle the special cases for the API that you plan to use:

function handleLeagueChange(value) {
  setLeague(value);
  if (league) {
    const selectedLeague = league.filter((el) => {
      if (el.league.name === value) {
        return el;
      }
    });

    if (selectedLeague) {
      setLeague(selectedLeague[0].league.name);
      setLeagueID(selectedLeague[0].league.id);
      setupSeasonSelect = selectedLeague[0].seasons.map((season) => {
        return {
          label: season.year,
          value: season.year,
        };
      });
      setFilteredSeasonOptions(setupSeasonSelect);
    }
  } else {
    return;
  }
}

function handleSeasonChange(value) {
  setSeason(value);
}

Submitting the settings selections

In the last article, we made a button in the block editor that fetches fresh data from the API. There’s no more need for it now that we have settings. Well, we do need it — just not where it currently is. Instead of having it directly in the block that’s rendered in the block editor, we’re going to move it to our PanelBody component to submit the settings selections.

So, back in LeagueSettings.js:

// When countriesList is loaded, show the country combo box
{ countriesList && (
  <ComboboxControl
    label="Choose country"
    value={country}
    options={filteredCountryOptions}
    onChange={(value) => handleCountryChange(value)}
    onInputChange={(inputValue) => {
      setFilteredCountryOptions(
        setupCountrySelect.filter((option) =>
          option.label
            .toLowerCase()
            .startsWith(inputValue.toLowerCase())
        )
      );
    }}
  />
)}

// When filteredLeagueOptions is set through handleCountryChange, show league combobox
{ filteredLeagueOptions && (
  <ComboboxControl
    label="Choose league"
    value={league}
    options={filteredLeagueOptions}
    onChange={(value) => handleLeagueChange(value)}
    onInputChange={(inputValue) => {
      setFilteredLeagueOptions(
        setupLeagueSelect.filter((option) =>
          option.label
            .toLowerCase()
            .startsWith(inputValue.toLowerCase())
        )
      );
    }}
  />
)}

// When filteredSeasonOptions is set through handleLeagueChange, show season combobox
{ filteredSeasonOptions && (
  <>
    <ComboboxControl
      label="Choose season"
      value={season}
      options={filteredSeasonOptions}
      onChange={(value) => handleSeasonChange(value)}
      onInputChange={
          (inputValue) => {
            setFilteredSeasonOptions(
              setupSeasonSelect.filter((option) =>
              option.label
              .toLowerCase()
              .startsWith(inputValue.toLowerCase()
            )
          );
        }
      }
    />

    // When season is set through handleSeasonChange, show the "Fetch data" button
    {
      season && (
        <button className="fetch-data" onClick={() => getData()}>Fetch data</button>
      )
    }
    </>
  </>
)}

Here’s the result!

We’re in a very good place with our block. We can render it in the block editor and the front end of the site. We can fetch data from an external API based on a selection of settings we created that filters the data. It’s pretty darn functional!

But there’s another thing we have to tackle. Right now, when we save the page or post that contains the block, the settings we selected for the block reset. In other words, those selections are not saved anywhere. There’s a little more work to make those selections persistent. That’s where we plan to go in the next article, so stay tuned.


Creating a Settings UI for a Custom WordPress Block originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/creating-a-settings-ui-for-a-custom-wordpress-block/feed/ 1 375098
Rendering External API Data in WordPress Blocks on the Back End https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-back-end/ https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-back-end/#respond Tue, 01 Nov 2022 12:57:22 +0000 https://css-tricks.com/?p=374381 This is a continuation of my last article about “Rendering External API Data in WordPress Blocks on the Front End”. In that last one, we learned how to take an external API and integrate it with a block that …


Rendering External API Data in WordPress Blocks on the Back End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
This is a continuation of my last article about “Rendering External API Data in WordPress Blocks on the Front End”. In that last one, we learned how to take an external API and integrate it with a block that renders the fetched data on the front end of a WordPress site.

The thing is, we accomplished this in a way that prevents us from seeing the data in the WordPress Block Editor. In other words, we can insert the block on a page but we get no preview of it. We only get to see the block when it’s published.

Let’s revisit the example block plugin we made in the last article. Only this time, we’re going to make use of the JavaScript and React ecosystem of WordPress to fetch and render that data in the back-end Block Editor as well.

Working With External APIs in WordPress Blocks

Where we left off

As we kick this off, here’s a demo where we landed in the last article that you can reference. You may have noticed that I used a render_callback method in the last article so that I can make use of the attributes in the PHP file and render the content.

Well, that may be useful in situations where you might have to use some native WordPress or PHP function to create dynamic blocks. But if you want to make use of just the JavaScript and React (JSX, specifically) ecosystem of WordPress to render the static HTML along with the attributes stored in the database, you only need to focus on the Edit and Save functions of the block plugin.

  • The Edit function renders the content based on what you want to see in the Block Editor. You can have interactive React components here.
  • The Save function renders the content based on what you want to see on the front end. You cannot have the the regular React components or the hooks here. It is used to return the static HTML that is saved into your database along with the attributes.

The Save function is where we’re hanging out today. We can create interactive components on the front-end, but for that we need to manually include and access them outside the Save function in a file like we did in the last article.

So, I am going to cover the same ground we did in the last article, but this time you can see the preview in the Block Editor before you publish it to the front end.

The block props

I intentionally left out any explanations about the edit function’s props in the last article because that would have taken the focus off of the main point, the rendering.

If you are coming from a React background, you will likely understand what is that I am talking about, but if you are new to this, I would recommend checking out components and props in the React documentation.

If we log the props object to the console, it returns a list of WordPress functions and variables related to our block:

Console log of the block properties.

We only need the attributes object and the setAttributes function which I am going to destructure from the props object in my code. In the last article, I had modified RapidAPI’s code so that I can store the API data through setAttributes(). Props are only readable, so we are unable to modify them directly.

Block props are similar to state variables and setState in React, but React works on the client side and setAttributes() is used to store the attributes permanently in the WordPress database after saving the post. So, what we need to do is save them to attributes.data and then call that as the initial value for the useState() variable.

The edit function

I am going to copy-paste the HTML code that we used in football-rankings.php in the last article and edit it a little to shift to the JavaScript background. Remember how we created two additional files in the last article for the front end styling and scripts? With the way we’re approaching things today, there’s no need to create those files. Instead, we can move all of it to the Edit function.

Full code
import { useState } from "@wordpress/element";
export default function Edit(props) {
  const { attributes, setAttributes } = props;
  const [apiData, setApiData] = useState(null);
    function fetchData() {
      const options = {
        method: "GET",
        headers: {
          "X-RapidAPI-Key": "Your Rapid API key",
          "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
        },
      };
      fetch(
        "https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39",
          options
      )
      .then((response) => response.json())
      .then((response) => {
        let newData = { ...response }; // Deep clone the response data
        setAttributes({ data: newData }); // Store the data in WordPress attributes
        setApiData(newData); // Modify the state with the new data
      })
      .catch((err) => console.error(err));
    }
    return (
      <div {...useBlockProps()}>
        <button onClick={() => getData()}>Fetch data</button>
        {apiData && (
          <>
          <div id="league-standings">
            <div
              className="header"
              style={{
                backgroundImage: `url(${apiData.response[0].league.logo})`,
              }}
            >
              <div className="position">Rank</div>
              <div className="team-logo">Logo</div>
              <div className="team-name">Team name</div>
              <div className="stats">
                <div className="games-played">GP</div>
                <div className="games-won">GW</div>
                <div className="games-drawn">GD</div>
                <div className="games-lost">GL</div>
                <div className="goals-for">GF</div>
                <div className="goals-against">GA</div>
                <div className="points">Pts</div>
              </div>
              <div className="form-history">Form history</div>
            </div>
            <div className="league-table">
              {/* Usage of [0] might be weird but that is how the API structure is. */}
              {apiData.response[0].league.standings[0].map((el) => {
                
                {/* Destructure the required data from all */}
                const { played, win, draw, lose, goals } = el.all;
                  return (
                    <>
                    <div className="team">
                      <div class="position">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.team.logo} />
                      </div>
                      <div className="team-name">{el.team.name}</div>
                      <div className="stats">
                        <div className="games-played">{played}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{goals.for}</div>
                        <div className="goals-against">{goals.against}</div>
                        <div className="points">{el.points}</div>
                      </div>
                      <div className="form-history">
                        {el.form.split("").map((result) => {
                          return (
                            <div className={`result-${result}`}>{result}</div>
                          );
                        })}
                      </div>
                    </div>
                    </>
                  );
                }
              )}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

I have included the React hook useState() from @wordpress/element rather than using it from the React library. That is because if I were to load the regular way, it would download React for every block that I am using. But if I am using @wordpress/element it loads from a single source, i.e., the WordPress layer on top of React.

This time, I have also not wrapped the code inside useEffect() but inside a function that is called only when clicking on a button so that we have a live preview of the fetched data. I have used a state variable called apiData to render the league table conditionally. So, once the button is clicked and the data is fetched, I am setting apiData to the new data inside the fetchData() and there is a rerender with the HTML of the football rankings table available.

You will notice that once the post is saved and the page is refreshed, the league table is gone. That is because we are using an empty state (null) for apiData‘s initial value. When the post saves, the attributes are saved to the attributes.data object and we call it as the initial value for the useState() variable like this:

const [apiData, setApiData] = useState(attributes.data);

The save function

We are going to do almost the same exact thing with the save function, but modify it a little bit. For example, there’s no need for the “Fetch data” button on the front end, and the apiData state variable is also unnecessary because we are already checking it in the edit function. But we do need a random apiData variable that checks for attributes.data to conditionally render the JSX or else it will throw undefined errors and the Block Editor UI will go blank.

Full code
export default function save(props) {
  const { attributes, setAttributes } = props;
  let apiData = attributes.data;
  return (
    <>
      {/* Only render if apiData is available */}
      {apiData && (
        <div {...useBlockProps.save()}>
        <div id="league-standings">
          <div
            className="header"
            style={{
              backgroundImage: `url(${apiData.response[0].league.logo})`,
            }}
          >
            <div className="position">Rank</div>
            <div className="team-logo">Logo</div>
            <div className="team-name">Team name</div>
            <div className="stats">
              <div className="games-played">GP</div>
              <div className="games-won">GW</div>
              <div className="games-drawn">GD</div>
              <div className="games-lost">GL</div>
              <div className="goals-for">GF</div>
              <div className="goals-against">GA</div>
              <div className="points">Pts</div>
            </div>
            <div className="form-history">Form history</div>
          </div>
          <div className="league-table">
            {/* Usage of [0] might be weird but that is how the API structure is. */}
            {apiData.response[0].league.standings[0].map((el) => {
              const { played, win, draw, lose, goals } = el.all;
                return (
                  <>
                  <div className="team">
                    <div className="position">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.team.logo} />
                      </div>
                      <div className="team-name">{el.team.name}</div>
                      <div className="stats">
                        <div className="games-played">{played}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{goals.for}</div>
                        <div className="goals-against">{goals.against}</div>
                        <div className="points">{el.points}</div>
                      </div>
                      <div className="form-history">
                        {el.form.split("").map((result) => {
                          return (
                            <div className={`result-${result}`}>{result}</div>
                          );
                        })}
                      </div>
                    </div>
                  </>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </>
  );
}

If you are modifying the save function after a block is already present in the Block Editor, it would show an error like this:

The football rankings block in the WordPress block Editor with an error message that the block contains an unexpected error.

That is because the markup in the saved content is different from the markup in our new save function. Since we are in development mode, it is easier to remove the bock from the current page and re-insert it as a new block — that way, the updated code is used instead and things are back in sync.

This situation of removing it and adding it again can be avoided if we had used the render_callback method since the output is dynamic and controlled by PHP instead of the save function. So each method has it’s own advantages and disadvantages.

Tom Nowell provides a thorough explanation on what not to do in a save function in this Stack Overflow answer.

Styling the block in the editor and the front end

Regarding the styling, it is going to be almost the same thing we looked at in the last article, but with some minor changes which I have explained in the comments. I’m merely providing the full styles here since this is only a proof of concept rather than something you want to copy-paste (unless you really do need a block for showing football rankings styled just like this). And note that I’m still using SCSS that compiles to CSS on build.

Editor styles
/* Target all the blocks with the data-title="Football Rankings" */
.block-editor-block-list__layout 
.block-editor-block-list__block.wp-block[data-title="Football Rankings"] {
  /* By default, the blocks are constrained within 650px max-width plus other design specific code */
  max-width: unset;
  background: linear-gradient(to right, #8f94fb, #4e54c8);
  display: grid;
  place-items: center;
  padding: 60px 0;

  /* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties really not needed :) */
  button.fetch-data {
    align-items: center;
    background-color: #ffffff;
    border: 1px solid rgb(0 0 0 / 0.1);
    border-radius: 0.25rem;
    box-shadow: rgb(0 0 0 / 0.02) 0 1px 3px 0;
    box-sizing: border-box;
    color: rgb(0 0 0 / 0.85);
    cursor: pointer;
    display: inline-flex;
    font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 16px;
    font-weight: 600;
    justify-content: center;
    line-height: 1.25;
    margin: 0;
    min-height: 3rem;
    padding: calc(0.875rem - 1px) calc(1.5rem - 1px);
    position: relative;
    text-decoration: none;
    transition: all 250ms;
    user-select: none;
    -webkit-user-select: none;
    touch-action: manipulation;
    vertical-align: baseline;
    width: auto;
    &:hover,
    &:focus {
      border-color: rgb(0, 0, 0, 0.15);
      box-shadow: rgb(0 0 0 / 0.1) 0 4px 12px;
      color: rgb(0, 0, 0, 0.65);
    }
    &:hover {
      transform: translateY(-1px);
    }
    &:active {
      background-color: #f0f0f1;
      border-color: rgb(0 0 0 / 0.15);
      box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;
      color: rgb(0 0 0 / 0.65);
      transform: translateY(0);
    }
  }
}
Front-end styles
/* Front-end block styles */
.wp-block-post-content .wp-block-football-rankings-league-table {
  background: linear-gradient(to right, #8f94fb, #4e54c8);
  max-width: unset;
  display: grid;
  place-items: center;
}

#league-standings {
  width: 900px;
  margin: 60px 0;
  max-width: unset;
  font-size: 16px;
  .header {
    display: grid;
    gap: 1em;
    padding: 10px;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: center;
    color: white;
    font-size: 16px;
    font-weight: 600;
    background-color: transparent;
    background-repeat: no-repeat;
    background-size: contain;
    background-position: right;

    .stats {
      display: flex;
      gap: 15px;
      &amp; &gt; div {
        width: 30px;
      }
    }
  }
}
.league-table {
  background: white;
  box-shadow:
    rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
    rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
  padding: 1em;
  .position {
    width: 20px;
  }
  .team {
    display: grid;
    gap: 1em;
    padding: 10px 0;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: center;
  }
  .team:not(:last-child) {
    border-bottom: 1px solid lightgray;
  }
  .team-logo img {
    width: 30px;
    top: 3px;
    position: relative;
  }
  .stats {
    display: flex;
    gap: 15px;
    &amp; &gt; div {
      width: 30px;
      text-align: center;
    }
  }
  .last-5-games {
    display: flex;
    gap: 5px;
    &amp; &gt; div {
      width: 25px;
      height: 25px;
      text-align: center;
      border-radius: 3px;
      font-size: 15px;
    &amp; .result-W {
      background: #347d39;
      color: white;
    }
    &amp; .result-D {
      background: gray;
      color: white;
    }
    &amp; .result-L {
      background: lightcoral;
      color: white;
    }
  }
}

We add this to src/style.scss which takes care of the styling in both the editor and the frontend. I will not be able to share the demo URL since it would require editor access but I have a video recorded for you to see the demo:


Pretty neat, right? Now we have a fully functioning block that not only renders on the front end, but also fetches API data and renders right there in the Block Editor — with a refresh button to boot!

But if we want to take full advantage of the WordPress Block Editor, we ought to consider mapping some of the block’s UI elements to block controls for things like setting color, typography, and spacing. That’s a nice next step in the block development learning journey.


Rendering External API Data in WordPress Blocks on the Back End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-back-end/feed/ 0 374381
Rendering External API Data in WordPress Blocks on the Front End https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-front-end/ https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-front-end/#comments Tue, 11 Oct 2022 16:15:48 +0000 https://css-tricks.com/?p=373952 There’ve been some new tutorials popping here on CSS-Tricks for working with WordPress blocks. One of them is an introduction to WordPress block development and it’s a good place to learn what blocks are and to register them in WordPress …


Rendering External API Data in WordPress Blocks on the Front End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There’ve been some new tutorials popping here on CSS-Tricks for working with WordPress blocks. One of them is an introduction to WordPress block development and it’s a good place to learn what blocks are and to register them in WordPress for use in pages and posts.

While the block basics are nicely covered in that post, I want to take it another step forward. You see, in that article, we learned the difference between rendering blocks in the back-end WordPress Block Editor and rendering them on the front-end theme. The example was a simple Pullquote Block that rendered different content and styles on each end.

Let’s go further and look at using dynamic content in a WordPress block. More specifically, let’s fetch data from an external API and render it on the front end when a particular block is dropped into the Block Editor.

This is part of a larger series where I want to cover all the points for working with external API data in a custom WordPress block.

Working With External APIs in WordPress Blocks

We’re going to build a block that outputs data that shows soccer (er, football) rankings pulled from Api-Football.

An ordered set of football team rankings showing team logos, names, and game results.
This is what we’re working for together.

There’s more than one way to integrate an API with a WordPress block! Since the article on block basics has already walked through the process of making a block from scratch, we’re going to simplify things by using the @wordpress/create-block package to bootstrap our work and structure our project.

Initializing our block plugin

First things first: let’s spin up a new project from the command line:

npx @wordpress/create-block football-rankings

I normally would kick a project like this off by making the files from scratch, but kudos to the WordPress Core team for this handy utility!

Once the project folder has been created by the command, we technically have a fully-functional WordPress block registered as a plugin. So, let’s go ahead and drop the project folder into the wp-content/plugins directory where you have WordPress installed (probably best to be working in a local environment), then log into the WordPress admin and activate it from the Plugins screen.

Now that our block is initialized, installed, and activated, go ahead and open up the project folder from at /wp-content/plugins/football-rankings. You’re going to want to cd there from the command line as well to make sure we can continue development.

These are the only files we need to concentrate on at the moment:

  • edit.js
  • index.js
  • football-rankings.php

The other files in the project are important, of course, but are inessential at this point.

Reviewing the API source

We already know that we’re using Api-Football which comes to us courtesy of RapidAPI. Fortunately, RapidAPI has a dashboard that automatically generates the required scripts we need to fetch the API data for the 2021 Premier League Standings.

A dashboard interface with three columns showing code and data from an API source.
The RapidAPI dashboard

If you want to have a look on the JSON structure, you can generate visual representation with JSONCrack.

Fetching data from the edit.js file

I am going to wrap the RapidAPI code inside a React useEffect() hook with an empty dependency array so that it runs only once when the page is loaded. This way, we prevent WordPress from calling the API each time the Block Editor re-renders. You can check that using wp.data.subscribe() if you care to.

Here’s the code where I am importing useEffect(), then wrapping it around the fetch() code that RapidAPI provided:

/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {WPElement} Element to render.
*/

import { useEffect } from "@wordpress/element";

export default function Edit(props) {
  const { attributes, setAttributes } = props;

  useEffect(() => {
    const options = {
      method: "GET",
      headers: {
        "X-RapidAPI-Key": "Your Rapid API key",
        "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
      },
    };

    fetch("https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39", options)
      .then( ( response ) => response.json() )
      .then( ( response ) => {
        let newData = { ...response };
        setAttributes( { data: newData } );
        console.log( "Attributes", attributes );
      })
      .catch((err) => console.error(err));
}, []);

  return (
    <p { ...useBlockProps() }>
      { __( "Standings loaded on the front end", "external-api-gutenberg" ) }
    </p>
  );
}

Notice that I have left the return function pretty much intact, but have included a note that confirms the football standings are rendered on the front end. Again, we’re only going to focus on the front end in this article — we could render the data in the Block Editor as well, but we’ll leave that for another article to keep things focused.

Storing API data in WordPress

Now that we are fetching data, we need to store it somewhere in WordPress. This is where the attributes.data object comes in handy. We are defining the data.type as an object since the data is fetched and formatted as JSON. Make sure you don’t have any other type or else WordPress won’t save the data, nor does it throw any error for you to debug.

We define all this in our index.js file:

registerBlockType( metadata.name, {
  edit: Edit,
  attributes: {
    data: {
      type: "object",
    },
  },
  save,
} );

OK, so WordPress now knows that the RapidAPI data we’re fetching is an object. If we open a new post draft in the WordPress Block Editor and save the post, the data is now stored in the database. In fact, if we can see it in the wp_posts.post_content field if we open the site’s database in phpMyAdmin, Sequel Pro, Adminer, or whatever tool you use.

Showing a large string of JSON output in a database table.
API output stored in the WordPress database

Outputting JSON data in the front end

There are multiple ways to output the data on the front end. The way I’m going to show you takes the attributes that are stored in the database and passes them as a parameter through the render_callback function in our football-rankings.php file.

I like keeping a separation of concerns, so how I do this is to add two new files to the block plugin’s build folder: frontend.js and frontend.css (you can create a frontend.scss file in the src directory which compiled to CSS in the build directory). This way, the back-end and front-end codes are separate and the football-rankings.php file is a little easier to read.

Referring back to the introduction to WordPress block development, there are editor.css and style.css files for back-end and shared styles between the front and back end, respectively. By adding frontend.scss (which compiles to frontend.css, I can isolate styles that are only intended for the front end.

Before we worry about those new files, here’s how we call them in football-rankings.php:

/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function create_block_football_rankings_block_init() {
  register_block_type( __DIR__ . '/build', array(
    'render_callback' => 'render_frontend'
  ));
}
add_action( 'init', 'create_block_football_rankings_block_init' );

function render_frontend($attributes) {
  if( !is_admin() ) {
    wp_enqueue_script( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.js');
    wp_enqueue_style( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.css' ); // HIGHLIGHT 15,16,17,18
  }
  
  ob_start(); ?>

  <div class="football-rankings-frontend" id="league-standings">
    <div class="data">
      <pre>
        <?php echo wp_json_encode( $attributes ) ?>
      </pre>
    </div>
    <div class="header">
      <div class="position">Rank</div>
      <div class="team-logo">Logo</div>
      <div class="team-name">Team name</div>
      <div class="stats">
        <div class="games-played">GP</div>
        <div class="games-won">GW</div>
        <div class="games-drawn">GD</div>
        <div class="games-lost">GL</div>
        <div class="goals-for">GF</div>
        <div class="goals-against">GA</div>
        <div class="points">Pts</div>
      </div>
      <div class="form-history">Last 5 games</div>
    </div>
    <div class="league-table"></div>
  </div>

  <?php return ob_get_clean();
}

Since I am using the render_callback() method for the attributes, I am going to handle the enqueue manually just like the Block Editor Handbook suggests. That’s contained in the !is_admin() condition, and is enqueueing the two files so that we avoid enqueuing them while using the editor screen.

Now that we have two new files we’re calling, we’ve gotta make sure we are telling npm to compile them. So, do that in package.json, in the scripts section:

"scripts": {
  "build": "wp-scripts build src/index.js src/frontend.js",
  "format": "wp-scripts format",
  "lint:css": "wp-scripts lint-style",
  "lint:js": "wp-scripts lint-js",
  "packages-update": "wp-scripts packages-update",
  "plugin-zip": "wp-scripts plugin-zip",
  "start": "wp-scripts start src/index.js src/frontend.js"
},

Another way to include the files is to define them in the block metadata contained in our block.json file, as noted in the introduction to block development:

"viewScript": [ "file:./frontend.js", "example-shared-view-script" ],
"style": [ "file:./frontend.css", "example-shared-style" ],

The only reason I’m going with the package.json method is because I am already making use of the render_callback() method.

Rendering the JSON data

In the rendering part, I am concentrating only on a single block. Generally speaking, you would want to target multiple blocks on the front end. In that case, you need to make use of document.querySelectorAll() with the block’s specific ID.

I’m basically going to wait for the window to load and grab data for a few key objects from JSON and apply them to some markup that renders them on the front end. I am also going to convert the attributes data to a JSON object so that it is easier to read through the JavaScript and set the details from JSON to HTML for things like the football league logo, team logos, and stats.

The “Last 5 games” column shows the result of a team’s last five matches. I have to manually alter the data for it since the API data is in a string format. Converting it to an array can help make use of it in HTML as a separate element for each of a team’s last five matches.

import "./frontend.scss";

// Wait for the window to load
window.addEventListener( "load", () => {
  // The code output
  const dataEl = document.querySelector( ".data pre" ).innerHTML;
  // The parent rankings element
  const tableEl = document.querySelector( ".league-table" );
  // The table headers
  const tableHeaderEl = document.querySelector( "#league-standings .header" );
  // Parse JSON for the code output
  const dataJSON = JSON.parse( dataEl );
  // Print a little note in the console
  console.log( "Data from the front end", dataJSON );
  
  // All the teams 
  let teams = dataJSON.data.response[ 0 ].league.standings[ 0 ];
  // The league logo
  let leagueLogoURL = dataJSON.data.response[ 0 ].league.logo;
  // Apply the league logo as a background image inline style
  tableHeaderEl.style.backgroundImage = `url( ${ leagueLogoURL } )`;
  
  // Loop through the teams
  teams.forEach( ( team, index ) => {
    // Make a div for each team
    const teamDiv = document.createElement( "div" );
    // Set up the columns for match results
    const { played, win, draw, lose, goals } = team.all;

    // Add a class to the parent rankings element
    teamDiv.classList.add( "team" );
    // Insert the following markup and data in the parent element
    teamDiv.innerHTML = `
      <div class="position">
        ${ index + 1 }
      </div>
      <div class="team-logo">
        <img src="${ team.team.logo }" />
      </div>
      <div class="team-name">${ team.team.name }</div>
      <div class="stats">
        <div class="games-played">${ played }</div>
        <div class="games-won">${ win }</div>
        <div class="games-drawn">${ draw }</div>
        <div class="games-lost">${ lose }</div>
        <div class="goals-for">${ goals.for }</div>
        <div class="goals-against">${ goals.against }</div>
        <div class="points">${ team.points }</div>
      </div>
      <div class="form-history"></div>
    `;
    
    // Stringify the last five match results for a team
    const form = team.form.split( "" );
    
    // Loop through the match results
    form.forEach( ( result ) => {
      // Make a div for each result
      const resultEl = document.createElement( "div" );
      // Add a class to the div
      resultEl.classList.add( "result" );
      // Evaluate the results
      resultEl.innerText = result;
      // If the result a win
      if ( result === "W" ) {
        resultEl.classList.add( "win" );
      // If the result is a draw
      } else if ( result === "D" ) {
        resultEl.classList.add( "draw" );
      // If the result is a loss
      } else {
        resultEl.classList.add( "lost" );
      }
      // Append the results to the column
      teamDiv.querySelector( ".form-history" ).append( resultEl );
    });

    tableEl.append( teamDiv );
  });
});

As far as styling goes, you’re free to do whatever you want! If you want something to work with, I have a full set of styles you can use as a starting point.

I styled things in SCSS since the @wordpress/create-block package supports it out of the box. Run npm run start in the command line to watch the SCSS files and compile them to CSS on save. Alternately, you can use npm run build on each save to compile the SCSS and build the rest of the plugin bundle.

View SCSS
body {
  background: linear-gradient(to right, #8f94fb, #4e54c8);
}

.data pre {
  display: none;
}

.header {
  display: grid;
  gap: 1em;
  padding: 10px;
  grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
  align-items: center;
  color: white;
  font-size: 16px;
  font-weight: 600;
  background-repeat: no-repeat;
  background-size: contain;
  background-position: right;
}

.frontend#league-standings {
  width: 900px;
  margin: 60px 0;
  max-width: unset;
  font-size: 16px;

  .header {
    .stats {
      display: flex;
      gap: 15px;

      &amp; &gt; div {
        width: 30px;
      }
    }
  }
}

.league-table {
  background: white;
  box-shadow:
    rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
    rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
  padding: 1em;

  .position {
    width: 20px;
  }

  .team {
    display: grid;
    gap: 1em;
    padding: 10px 0;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: center;
  }

  .team:not(:last-child) {
    border-bottom: 1px solid lightgray;
  }

  .team-logo img {
    width: 30px;
  }

  .stats {
    display: flex;
    gap: 15px;
  }

  .stats &gt; div {
    width: 30px;
    text-align: center;
  }

  .form-history {
    display: flex;
    gap: 5px;
  }

  .form-history &gt; div {
    width: 25px;
    height: 25px;
    text-align: center;
    border-radius: 3px;
    font-size: 15px;
  }

  .form-history .win {
    background: #347d39;
    color: white;
  }

  .form-history .draw {
    background: gray;
    color: white;
  }

  .form-history .lost {
    background: lightcoral;
    color: white;
  }
}

Here’s the demo!

Check that out — we just made a block plugin that fetches data and renders it on the front end of a WordPress site.

We found an API, fetch()-ed data from it, saved it to the WordPress database, parsed it, and applied it to some HTML markup to display on the front end. Not bad for a single tutorial, right?

Again, we can do the same sort of thing so that the rankings render in the Block Editor in addition to the theme’s front end. But hopefully keeping this focused on the front end shows you how fetching data works in a WordPress block, and how the data can be structured and rendered for display.


Rendering External API Data in WordPress Blocks on the Front End originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/rendering-external-api-data-in-wordpress-blocks-on-the-front-end/feed/ 8 373952
Adding Fluid Typography Support to WordPress Block Themes https://css-tricks.com/fluid-typography-wordpress-block-themes/ https://css-tricks.com/fluid-typography-wordpress-block-themes/#respond Fri, 07 Oct 2022 13:19:23 +0000 https://css-tricks.com/?p=373905 Fluid typography is a fancy way of “describing font properties, such as size or line height, that scale fluidly according to the size of the viewport”. It’s also known by other names, like responsive typography, flexible type, fluid type, …


Adding Fluid Typography Support to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Fluid typography is a fancy way of “describing font properties, such as size or line height, that scale fluidly according to the size of the viewport”. It’s also known by other names, like responsive typography, flexible type, fluid type, viewport sized typography, fluid typography, and even responsive display text.

Here is an example of fluid typography that you can play live (courtesy of MDN documentation). CSS-Tricks has covered fluid typography extensively as well. But the point here is not to introduce you to fluid typography, but how to use it. More specifically, I want to show you how to implement fluid typography in WordPress 6.1 which recently introduced a fluid type feature directly in the WordPress Block Editor.

Open up your style.css file, slap in a style rule with fancy clamp()-ing on the font-size property, and good to go, right? Sure, that’ll give you fluid text, but to get Block Editor controls that make it possible to apply fluid type anywhere on your WordPress site? That requires a different approach in these block-ified days.

Fluid typography support in Gutenberg

Some WordPress theme developers have been using the clamp() function to define a fluid font-size, in their WordPress themes, even in newer “block” themes such as Twenty Twenty-Two, Twenty Twenty-Three, and others.

But the Gutenberg plugin — the one that contains experimental development for WordPress Block and Site Editor features — introduced support for fluid typography starting in version 13.8. That opened the door for implementing at a theme level so that fluid type can be applied to specific elements and blocks directly in the Block Editor. CSS-Tricks was even given a shout-out in the Pull Request that merged the feature.

That work became part of WordPress Core in WordPress 6.1. Rich Tabor, one of the earlier advocates of fluid typography in the Block Editor says:

[Fluid typography] is also a part of making WordPress more powerful, while not more complicated (which we all know is quite the challenge). […] Fluid typography just works. Actually, I think it works great.

This Make WordPress post highlights the approach taken to support the feature at the block level so that a fluid font size is applied to blocks dynamically by default. There are some benefits to this, of course:

  • It provides a way for theme authors to activate fluid typography without worrying about implementing it in code.
  • It applies fluid typography to specific typographical entities, such as elements or blocks in a maintainable and reusable way.
  • It allows flexibility in terms of font size units (e.g. px, rem, em, and %).

Now that this new feature is available in the WordPress Block Editor by default, theme authors can apply uniform fluid typography without writing additional code.

Blocks that support typography and spacing settings

Gutenberg 14.1 released on September 16, 2022, and introduced typographic settings on a bunch of blocks. That means the text settings for those blocks were set in CSS before and had to be changed in CSS as well. But those blocks now provide font and spacing controls in the Block Editor interface.

Illustrated list of WordPress blocks that received font and spacing controls in the Gutenberg plugin. There are 31 total blocks.

That work is currently slated to be added to WordPress 6.1, as detailed in this Make WordPress blog post. And with it is an expanded number of blocks that with typography support.

Illustrated list of 60 WordPress blocks gaining typography and font size support in WordPress 6.1.
WordPress blocks that will support typography settings in the upcoming WordPress 6.1 release.

Declaring fluid type in a WordPress block theme

So, how do we put this new fluid typography to use in WordPress? The answer is in theme.json, a new-ish file specific to block themes that contains a bunch of theme configurations in key:value pairs.

Let’s look at a rule for a large font in theme.json where contentSize: 768px and we’re working with a widesize: 1600px layout. This is how we can specify a CSS font-size using the clamp() function:

"settings": {
  "appearanceTools": true,
  "layout": {
    "contentSize": "768px",
    "wideSize": "1600px"
  },
  "typography": {
    "fontSizes": [ 
      {
        "name": "Large",
        "size": "clamp(2.25rem, 6vw, 3rem)",
        "slug": "large"
      }
    ]
  }
}

As of WordPress 6.1, only rem, em and px units are supported.

That’s great and works, but with the new fluid type feature we would actually use a different approach. First, we opt into fluid typography on settings.typography, which has a new fluid property:

"settings": {
  "typography": {
    "fluid": true
  }
}

Then we specify our settings.fontSizes like before, but with a new fluidSize property where we can set the min and max size values for our fluid type range.

"settings": {
  "appearanceTools": true,
  "layout": {
    "contentSize": "768px",
    "wideSize": "1600px"
  },
  "typography": {
    "fontSizes": [ 
      {
        "size": "2.25rem",
        "fluidSize": {
          "min": "2.25rem",
          "max": "3rem"
        },
        "slug": "large",
        "name": "Large"
      }
    ]
  }
}

That’s really it. We just added fluid type to a font size called “Large” with a range that starts at 2.25rem and scales up to 3rem. Now, we can apply the “Large” font to any block with font settings.

How does this works under the hood? Rich Tabor offers a nice explanation, as does this Pull Request in GitHub. In short, WordPress converts the theme.json properties into the following CSS rule:

.has-large-font-size {
  font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px);
}

…which is applied to the element, say a Paragraph Block:

<p class="has-large-font-size">...</p>

Initially, I found it hard to understand and wrap around in my mind the concept of the CSS clamp() function without also learning about the min(), max(), and calc() functions. This calculator tool helped me quite a bit, especially for determining which values to use in my own theme projects.

For demonstration purposes, let’s use the calculator to define our font-size range so that the size is 36px at a 768px viewport width and 48px at a 1600px viewport width.

Entering values into the online calculator for fluid typography.

The calculator automatically generates the following CSS:

/* 36px @ 768px increasing to 48px @ 1600px */
font-size: clamp(36px, calc(2.25rem + ((1vw - 7.68px) * 1.4423)), 48px);

The calculator provide options to select input units as px, rem, and em. If we select rem unit, the calculator generates the following clamp() value:

/* 2.25rem @ 48rem increasing to 3rem @ 100rem */
font-size: clamp(2.25rem, calc(2.25rem + ((1vw - 0.48rem) * 1.4423)), 3rem);

So, those minimum and maximum values correspond to the the fluidSize.min and fluidSize.max values in theme.json. The min value is applied at viewports that are 768px wide and below. Then the font-size scales up as the viewport width grows. If the viewport is wider than 1600px, the max is applied and the font-size is capped there.

Examples

There are detailed testing instructions in the merged Pull Request that introduced the feature. There are even more testing instructions from Justin Tadlock’s pre-prelease post on Make WordPress.

Example 1: Setting a new fluid font setting

Let’s start with Justin’s set of instructions. I used in a modified version of the default Twenty Twenty-Three theme that is currently under development.

First, let’s make sure we’re running the Gutenberg plugin (13.8 and up) or WordPress 6.1, then opt into fluid type on the settings.typography.fluid property in the theme.json file:

{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "layout": {
      "contentSize": "768px",
      "wideSize": "1600px"
    },
    "typography": {
      "fluid": true
    }
  }
}

Now, let’s drop the settings.typography.fontSizes examples in there:

{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "layout": {
      "contentSize": "768px",
      "wideSize": "1600px"
    },
    "typography": {
      "fluid": true
      "fontSizes": [
        {
          "name": "Normal",
          "size": "1.125rem",
          "fluid": {
            "min": "1rem",
            "max": "1.5rem"
          },
          "slug": "normal"
        }
      ]
    }
  }
}

If everything is working correctly, we can now head into the WordPress Block Editor and apply the “Normal” font setting to our block:

The WordPress Block Editor interface showing a paragraph block and the fluid typography settings for it.

Nice! And if we save and inspect that element on the front end, this is the markup:

Inspecting the WordPress Paragraph block in DevTools.

Very good. Now let’s make sure the CSS is actually there:

DevTools showing the font-size custom property for the WordPress Paragraph block's fluid typography.

Good, good. Let’s expose that CSS custom property to see if it’s really clampin’ things:

Revealing the custom property value in DevTools, showing a CSS clamp function.

Looks like everything is working just as we want it! Let’s look at another example…

Example 2: Excluding a font setting from fluid type

This time, let’s follow the instructions from the merged Pull Request with a nod to this example by Carolina Nymark that shows how we can disable fluid type on a specific font setting.

I used the empty theme as advised in the instructions and opened up the theme.json file for testing. First, we opt into fluid type exactly as we did before:

{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "layout": {
      "contentSize": "768px",
      "wideSize": "1000px"
    },
    "typography": {
      "fluid": true
    }
  }
}

This time, we’re working with smaller wideSize value of 1000px instead of 1600px. This should allow us to see fluid type working in an exact range.

OK, on to defining some custom font sizes under settings.typography.fontSizes:

{
  "version": 2,
  "settings": {
    "typography": {
      "fluid": true,
      "fontSizes": [
        {
          "size": ".875rem",
          "fluid": {
            "min": "0.875rem",
            "max": "1rem"
        },
          "slug": "small",
          "name": "Small"
        },
        {
          "size": "1rem",
          "fluid": {
            "min": "1rem",
            "max": "1.5rem"
          },
          "slug": "normal",
          "name": "Normal"
        },
        {
          "size": "1.5rem",
          "fluid": {
            "min": "1.5rem",
            "max": "2rem"
          },
          "slug": "large",
          "name": "Large"
        },
        {
          "size": "2.25rem",
          "fluid": false,
          "slug": "x-large",
          "name": "Extra large"
        }
      ]
    }
  }
}

Notice that we’ve applied the fluid type feature only on the “Normal”, “Medium”, and “Large” font settings. “Extra Large” is the odd one out where the fluid object is set to false.

the WordPress Block Editor interface with four Paragraph blocks, each at a different font size setting.

What WordPress does from here — via the Gutenberg style engine — is apply the properties we set into CSS clamp() functions for each font size setting that has opted into fluid type and a single size value for the Extra Large setting:

--wp--preset--font-size--small: clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 0.24), 1rem);
--wp--preset--font-size--medium: clamp(1rem, 1rem + ((1vw - 0.48rem) * 0.962), 1.5rem);
--wp--preset--font-size--large: clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 0.962), 2rem);
--wp--preset--font-size--x-large: 2.25rem;

Let’s check the markup on the front end:

Inspecting the WordPress Paragraph blocks in DevTools.

Good start! Let’s confirm that the .has-x-large-font-size class is excluded from fluid type:

Showing the font-size custom property for the Extra Large font setting in DevTools.

If we expose the --wp--preset--font-size--x-large variable, we’ll see it’s set to 2.25rem.

Revealing the Extra Large font size custom property value, showing 2.25rem.

That’s exactly what we want!

Block themes that support fluid typography

Many WordPress themes already make use of the clamp() function for fluid type in both block and classic themes. A good example of fluid typography use is the recently released Twenty Twenty-Three default theme.

I’ve reviewed all the block themes from WordPress Block Theme directory, examining theme.json file of each theme and to see just how many block themes currently support fluid typography — not the new feature since it’s still in the Gutenberg plugin as of this writing — using the CSS clamp() function. Of the 146 themes I reviewed, the majority of them used a clamp() function to define spacing. A little more than half of them used clamp() to define font sizes. The Alara theme is the only one to use clamp() for defining the layout container sizes.

Understandably, only a few recently released themes contain the new fluid typography feature. But here are the ones I found that define it in theme.json:

And if you read my previous post here on CSS-Tricks, the TT2 Gopher Blocks theme I used for the demo has also been updated to support the fluid typography feature.

Selected reactions to the WordPress fluid typography features

Having fluid typography in WordPress at the settings level is super exciting! I thought I’d share some thoughts from folks in the WordPress developer community who have commented on it.

Matias Ventura, the lead architect of the Gutenberg project:

Rich Tabor:

As one of the bigger efforts towards making publishing beautifully rich pages in WordPress, fluid typography is a pretty big experience win for both the folks building with WordPress — and those consuming the content.

Automattic developer Ramon Dodd commented in the Pull Request:

Contrast that idea with font sizes that respond to specific viewport sizes, such as those defined by media queries, but do nothing in between those sizes. theme.json already allows authors to insert their own fluid font size values. This won’t change, but this PR offers it to folks who don’t want to worry about the implementation details.

Nick Croft, author of GenesisWP:

Brian Garner, designer and principal developer advocate at WPEngine:

A few developers think some features should be an opt-in. Jason Crist of Automattic says:

I love the power of fluid typography, however I also don’t believe that it should just be enabled by default. It’s usage (and the details of it) are important design decisions that should be made carefully.

You can also find a bunch more comments in the official testing instructions for the feature.

Wrapping up

The fluid typography feature in WordPress is still in active development at the time of this writing. So, right now, theme authors should proceed to use it, but with caution and expect some possible changes before it is officially released. Justin cautions theme authors using this feature and suggests to keep eye on the following two GitHub issues:

There is also still lots of ongoing work on typography and other design-related WordPress tools. If you’re interested, watch this typography tracking GitHub ticket and design tools related GitHub issues.

Resources

I used the following articles when researching fluid type and how WordPress is implementing it as a feature.

Tutorials and opinions

CSS-Tricks


Adding Fluid Typography Support to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/fluid-typography-wordpress-block-themes/feed/ 0 373905
Getting Started With WordPress Block Development https://css-tricks.com/getting-started-with-wordpress-block-development/ https://css-tricks.com/getting-started-with-wordpress-block-development/#comments Fri, 30 Sep 2022 13:04:56 +0000 https://css-tricks.com/?p=373732 Let’s acknowledge that developing for WordPress is weird right now. Whether you’re new to WordPress or have worked with it for eons, the introduction of “Full-Site Editing” (FSE) features, including the Block Editor (WordPress 5.0) and the Site Editor (WordPress …


Getting Started With WordPress Block Development originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let’s acknowledge that developing for WordPress is weird right now. Whether you’re new to WordPress or have worked with it for eons, the introduction of “Full-Site Editing” (FSE) features, including the Block Editor (WordPress 5.0) and the Site Editor (WordPress 5.9), have upended the traditional way we build WordPress themes and plugins.

Even though it’s been five years since we met the Block Editor for the first time, developing for it is difficult because documentation is either lacking or outdated. That’s more of a statement on how fast FSE features are moving, something Geoff lamented in a recent post.

Case in point: In 2018, an introductory series about getting into Gutenberg development was published right here on CSS-tricks. Times have changed since then, and, while that style of development does still work, it is not recommended anymore (besides, the create-guten-block project it’s based on is also no longer maintained).

In this article, I intend to help you get started with WordPress block development in a way that follows the current methodology. So, yes, things could very well change after this is published. But I’m going to try and focus on it in a way that hopefully captures the essence of block development because even though the tools might evolve over time, the core ideas are likely to remain the same.

The WordPress Block Editor interface with highlighted areas showing three key features.
The Gutenberg Editor: (1) The block inserter, (2) the content area, and (3) the settings sidebar
Credit: WordPress Block Editor Handbook

What are WordPress blocks, exactly?

Let’s start by airing out some confusion with what we mean by terms like blocks. All of the development that went into these features leading up to WordPress 5.0 was codenamed “Gutenberg” — you know, the inventor of the printing press.

Since then, “Gutenberg” has been used to describe everything related to blocks, including the Block Editor and Site Editor, so it’s gotten convoluted to the extent that some folks despise the name. To top it all off, there’s a Gutenberg plugin where experimental features are tested for possible inclusion. And if you think calling all of this “Full-Site Editing” would solve the issue, there are concerns with that as well.

So, when we refer to “blocks” in this article what we mean are components for creating content in the WordPress Block Editor. Blocks are inserted into a page or post and provide the structure for a particular type of content. WordPress ships with a handful of “core” blocks for common content types, like Paragraph, List, Image, Video, and Audio, to name a few.

Apart from these core blocks, we can create custom blocks too. That is what WordPress block development is about (there’s also filtering core blocks to modify their functionality, but you likely won’t be needing that just yet).

What blocks do

Before we dive into creating blocks, we must first get some sense of how blocks work internally. That will definitely save us a ton of frustration later on.

The way I like to think about a block is rather abstract: to me, a block is an entity, with some properties (called attributes), that represents some content. I know this sounds pretty vague, but stay with me. A block basically manifests itself in two ways: as a graphical interface in the block editor or as a chunk of data in the database.

When you open up the WordPress Block Editor and insert a block, say a Pullquote block, you get a nice interface. You can click into that interface and edit the quoted text. The Settings panel to the right side of the Block Editor UI provides options for adjusting the text and setting the block’s appearance.

The Pullquote block that is included in WordPress Core

When you are done creating your fancy pullquote and hit Publish, the entire post gets stored in the database in the wp_posts table. This isn’t anything new because of Gutenberg. That’s how things have always worked — WordPress stores post content in a designated table in the database. But what’s new is that a representation of the Pullquote block is part of the content that gets stored in post_content field of the wp_posts table.

What does this representation look like? Have a look:

<!-- wp:pullquote {"textAlign":"right"} -->
<figure class="wp-block-pullquote has-text-align-right">
  <blockquote>
    <p>It is not an exaggeration to say that peas can be described as nothing less than perfect spheres of joy.</p>
    <cite>The Encyclopedia of world peas</cite>
  </blockquote>
</figure>
<!-- /wp:pullquote -->

Looks like plain HTML, right?! This, in technical lingo, is the “serialized” block. Notice the JSON data in the HTML comment, "textAlign": "right". That’s an attribute — a property associated with the block.

Let’s say that you close the Block Editor, and then some time later, open it again. The content from the relevant post_content field is retrieved by the Block Editor. The editor then parses the retrieved content, and wherever it encounters this:

<!-- wp:pullquote {"textAlign":"right"} -->...<!-- /wp:pullquote -->

…it says out loud to itself:

OK, that seems like a Pullquote block to me. Hmm.. it’s got an attribute too… I do have a JavaScript file that tells me how to construct the graphical interface for a Pullquote block in the editor from its attributes. I should do that now to render this block in all its glory.

As a block developer, your job is to:

  1. Tell WordPress that you want to register a specific type of block, with so-and-so details.
  2. Provide the JavaScript file to the Block Editor that will help it render the block in the editor while also “serializing” it to save it in the database.
  3. Provide any additional resources the block needs for its proper functionality, e.g. styles and fonts.

One thing to note is that all of this conversion from serialized data to graphical interface — and vice versa — takes place only in the Block Editor. On the front end, the content is displayed exactly the way it is stored. Therefore, in a sense, blocks are a fancy way of putting data in the database.

Hopefully, this gives you some clarity as to how a block works.

Diagram outlining the post editor states and how data is saved to a database and parsed for rendering.

Blocks are just plugins

Blocks are just plugins. Well, technically, you can put blocks in themes and you can put multiple blocks in a plugin. But, more often than not, if you want to make a block, you’re going to be making a plugin. So, if you’ve ever created a WordPress plugin, then you’re already part-way there to having a handle on making a WordPress block.

But let’s assume for a moment that you’ve never set up a WordPress plugin, let alone a block. Where do you even start?

Setting up a block

We have covered what blocks are. Let’s start setting things up to make one.

Make sure you have Node installed

This will give you access to npm and npx commands, where npm installs your block’s dependencies and helps compile stuff, while npx runs commands on packages without installing them. If you’re on macOS, you probably already have Node and can can use nvm to update versions. If you’re on Windows, you’ll need to download and install Node.

Create a project folder

Now, you might run into other tutorials that jump straight into the command line and instruct you to install a package called @wordpress/create-block. This package is great because it spits out a fully formed project folder with all the dependencies and tools you need to start developing.

I personally go this route when setting up my own blocks, but humor me for a moment because I want to cut through the opinionated stuff it introduces and focus just on the required bits for the sake of understanding the baseline development environment.

These are the files I’d like to call out specifically:

  • readme.txt: This is sort of like the front face of the plugin directory, typically used to describe the plugin and provide additional details on usage and installation. If you submit your block to the WordPress Plugin Directory, this file helps populate the plugin page. If you plan on creating a GitHub repo for your block plugin, then you might also consider a README.md file with the same information so it displays nicely there.
  • package.json: This defines the Node packages that are required for development. We’ll crack it open when we get to installation. In classic WordPress plugin development, you might be accustomed to working with Composer and a composer.json file instead. This is the equivalent of that.
  • plugin.php: This is the main plugin file and, yes, it’s classic PHP! We’ll put our plugin header and metadata in here and use it to register the plugin.

In addition to these files, there’s also the src directory, which is supposed to contain the source code of our block.

Having these files and the src directory is all you need to get started. Out of that group, notice that we technically only need one file (plugin.php) to make the plugin. The rest either provide information or are used to manage the development environment.

The aforementioned @wordpress/create-block package scaffolds these files (and more) for us. You can think of it as an automation tool instead of a necessity. Regardless, it does make the job easier, so you can take the liberty of scaffolding a block with it by running:

npx @wordpress/create-block

Install block dependencies

Assuming you have the three files mentioned in the previous section ready, it’s time to install the dependencies. First, we need to specify the dependencies we will need. We do that by editing the package.json. While using the @wordpress/create-block utility, the following is generated for us (comments added; JSON does not support comments, so remove the comments if you’re copying the code):

{
  // Defines the name of the project
  "name": "block-example",
  // Sets the project version number using semantic versioning
  "version": "0.1.0",
  // A brief description of the project
  "description": "Example block scaffolded with Create Block tool.",
  // You could replace this with yourself
  "author": "The WordPress Contributors",
  // Standard licensing information
  "license": "GPL-2.0-or-later",
  // Defines the main JavaScript file
  "main": "build/index.js",
  // Everything we need for building and compiling the plugin during development
  "scripts": {
    "build": "wp-scripts build",
    "format": "wp-scripts format",
    "lint:css": "wp-scripts lint-style",
    "lint:js": "wp-scripts lint-js",
    "packages-update": "wp-scripts packages-update",
    "plugin-zip": "wp-scripts plugin-zip",
    "start": "wp-scripts start"
  },
  // Defines which version of the scripts packages are used (24.1.0 at time of writing)
  // https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/
  "devDependencies": {
    "@wordpress/scripts": "^24.1.0"
  }
}
View without comments
{
  "name": "block-example",
  "version": "0.1.0",
  "description": "Example block scaffolded with Create Block tool.",
  "author": "The WordPress Contributors",
  "license": "GPL-2.0-or-later",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "format": "wp-scripts format",
    "lint:css": "wp-scripts lint-style",
    "lint:js": "wp-scripts lint-js",
    "packages-update": "wp-scripts packages-update",
    "plugin-zip": "wp-scripts plugin-zip",
    "start": "wp-scripts start"
  },
  "devDependencies": {
    "@wordpress/scripts": "^24.1.0"
  }
}

The @wordpress/scripts package is the main dependency here. As you can see, it’s a devDependency meaning that it aids in development. How so? It exposes the wp-scripts binary that we can use to compile our code, from the src directory to the build directory, among other things.

There are a number of other packages that WordPress maintains for various purposes. For example, the @wordpress/components package provides several pre-fab UI components for the WordPress Block Editor that can be used for creating consistent user experiences for your block that aligns with WordPress design standards.

You don’t actually need to install these packages, even if you want to use them. This is because these @wordpress dependencies aren’t bundled with your block code. Instead, any import statements referencing code from utility packages — like @wordpress/components — are used to construct an “assets” file, during compilation. Moreover, these import statements are converted to statements mapping the imports to properties of a global object. For example, import { __ } from "@wordpress/i18n" is converted to a minified version of const __ = window.wp.i18n.__. (window.wp.i18n being an object that is guaranteed to be available in the global scope, once the corresponding i18n package file is enqueued).

During block registration in the plugin file, the “assets” file is implicitly used to tell WordPress the package dependencies for the block. These dependencies are automatically enqueued. All of this is taken care of behind the scenes, granted you are using the scripts package. That being said, you can still choose to locally install dependencies for code completion and parameter info in your package.json file:

// etc.
"devDependencies": {
  "@wordpress/scripts": "^24.1.0"
},
"dependencies": {
  "@wordpress/components": "^19.17.0"
}

Now that package.json is set up, we should be able to install all those dependencies by navigating to the project folder in the command line and running npm install.

Terminal output after running the install command. 1,296 packages were installed.

Add the plugin header

If you’re coming from classic WordPress plugin development, then you probably know that all plugins have a block of information in the main plugin file that helps WordPress recognize the plugin and display information about it on the Plugins screen of the WordPress admin.

Here’s what @wordpress/create-block generated for me in for a plugin creatively called “Hello World”:

<?php
/**
 * Plugin Name:       Block Example
 * Description:       Example block scaffolded with Create Block tool.
 * Requires at least: 5.9
 * Requires PHP:      7.0
 * Version:           0.1.0
 * Author:            The WordPress Contributors
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       css-tricks
 *
 * @package           create-block
 */

That’s in the main plugin file, which you can call whatever you’d like. You might call it something generic like index.php or plugin.php. The create-block package automatically calls it whatever you provide as the project name when installing it. Since I called this example “Block Example”, the package gave us a block-example.php file with all this stuff.

You’re going to want to change some of the details, like making yourself the author and whatnot. And not all of that is necessary. If I was rolling this from “scratch”, then it might look something closer to this:

<?php
/**
 * Plugin Name:       Block Example
 * Plugin URI:        https://css-tricks.com
 * Description:       An example plugin for learning WordPress block development.
 * Version:           1.0.0
 * Author:            Arjun Singh
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       css-tricks
 */

I won’t get into the exact purpose of each line since that’s already a well-established pattern in the WordPress Plugin Handbook.

The file structure

We’ve already looked at the required files for our block. But if you’re using @wordpress/create-block, you will see a bunch of other files in the project folder.

Here’s what’s in there at the moment:

block-example/
├── build
├── node_modules
├── src/
│   ├── block.json
│   ├── edit.js
│   ├── editor.scss
│   ├── index.js
│   ├── save.js
│   └── style.scss
├── .editorconfig
├── .gitignore
├── block-example.php
├── package-lock.json
├── package.json
└── readme.txt

Phew, that’s a lot! Let’s call out the new stuff:

  • build/: This folder received the compiled assets when processing the files for production use.
  • node_modules: This holds all the development dependencies we installed when running npm install.
  • src/: This folder holds the plugin’s source code that gets compiled and sent to the build directory. We’ll look at each of the files in here in just a bit.
  • .editorconfig: This contains configurations to adapt your code editor for code consistency.
  • .gitignore: This is a standard repo file that identifies local files that should be excluded from version control tracking. Your node_modules should definitely be included in here.
  • package-lock.json: This is an auto-generated file containing for tracking updates to the required packages we installed with npm install.

Block metadata

I want to dig into the src directory with you but will focus first on just one file in it: block.json. If you’ve used create-block , it’s already there for you; if not, go ahead and create it. WordPress is leaning in hard to make this the standard, canonical way to register a block by providing metadata that provides WordPress context to both recognize the block and render it in the Block Editor.

Here’s what @wordpress/create-block generated for me:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 2,
  "name": "create-block/block example",
  "version": "0.1.0",
  "title": "Block Example",
  "category": "widgets",
  "icon": "smiley",
  "description": "Example block scaffolded with Create Block tool.",
  "supports": {
    "html": false
  },
  "textdomain": "css-tricks",
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css"
}

There’s actually a bunch of different information we can include here, but all that’s actually required is name and title. A super minimal version might look like this:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 2,
  "name": "css-tricks/block-example",
  "version": "1.0.0",
  "title": "Block Example",
  "category": "text",
  "icon": "format-quote",
  "editorScript": "file:./index.js",
}
  • $schema defines the schema formatting used to validate the content in the file. It sounds like a required thing, but it’s totally optional as it allows supporting code editors to validate the syntax and provide other additional affordances, like tooltip hints and auto-completion.
  • apiVersion refers to which version of the Block API the plugin uses. Today, Version 2 is the latest.
  • name is a required unique string that helps identify the plugin. Notice that I’ve prefixed this with css-tricks/ which I’m using as a namespace to help avoid conflicts with other plugins that might have the same name. You might choose to use something like your initials instead (e.g. as/block-example).
  • version is something WordPress suggests using as a cache-busting mechanism when new versions are released.
  • title is the other required field, and it sets the name that’s used wherever the plugin is displayed.
  • category groups the block with other blocks and displays them together in the Block Editor. Current existing categories include text, media, design, widgets, theme, and embed, and you can even create custom categories.
  • icon lets you choose something from the Dashicons library to visually represent your block in the Block Editor. I’m using the format-quote icon since we’re making our own pullquote sort of thing in this example. It’s nice we can leverage existing icons rather than having to create our own, though that’s certainly possible.
  • editorScript is where the main JavaScript file, index.js, lives.

Register the block

One last thing before we hit actual code, and that’s to register the plugin. We just set up all that metadata and we need a way for WordPress to consume it. That way, WordPress knows where to find all the plugin assets so they can be enqueued for use in the Block Editor.

Registering the block is a two-fold process. We need to register it both in PHP and in JavaScript. For the PHP side, open up the main plugin file (block-example.php in this case) and add the following right after the plugin header:

function create_block_block_example_block_init() {
  register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'create_block_block_example_block_init' );

This is what the create-block utility generated for me, so that’s why the function is named the way it is. We can use a different name. The key, again, is avoiding conflicts with other plugins, so it’s a good idea to use your namespace here to make it as unique as possible:

function css_tricks_block_example_block_init() {
  register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'css_tricks_block_example_block_init' );

Why are we pointing to the build directory if the block.json with all the block metadata is in src? That’s because our code still needs to be compiled. The scripts package processes the code from files in the src directory and places the compiled files used in production in the build directory, while also copying the block.json file in the process.

Alright, let’s move over to the JavaScript side of registering the block. Open up src/index.js and make sure it looks like this:

import { registerBlockType } from "@wordpress/blocks";

import metadata from "./block.json";
import Edit from "./edit.js";
import Save from "./save.js";

const { name } = metadata;

registerBlockType(name, {
  edit: Edit,
  save: Save,
});

We’re getting into React and JSX land! This tells WordPress to:

  • Import the registerBlockType module from the @wordpress/blocks package.
  • Import metadata from block.json.
  • Import the Edit and Save components from their corresponding files. We’ll be putting code into those files later.
  • Register the the block, and use the Edit and Save components for rendering the block and saving its content to the database.

What’s up with the edit and save functions? One of the nuances of WordPress block development is differentiating the “back end” from the “front end” and these functions are used to render the block’s content in those contexts, where edit handles back-end rendering and save writes the content from the Block Editor to the database for rendering the content on the front end of the site.

A quick test

We can do some quick work to see our block working in the Block Editor and rendered on the front end. Let’s open index.js again and use the edit and save functions to return some basic content that illustrates how they work:

import { registerBlockType } from "@wordpress/blocks";
import metadata from "./block.json";

const { name } = metadata;

registerBlockType(name, {
  edit: () => {
    return (
      "Hello from the Block Editor"
    );
  },
  save: () => {
    return (
      "Hello from the front end"
    );
  }
});

This is basically a stripped-down version of the same code we had before, only we’re pointing directly to the metadata in block.json to fetch the block name, and left out the Edit and Save components since we’re running the functions directly from here.

We can compile this by running npm run build in the command line. After that, we have access to a block called “Block Example” in the Block Editor:

The WordPress Block Editor with the block inserter panel open and the pullquote block inserted into the content area. It reads hello from the back end.

If we drop the block into the content area, we get the message we return from the edit function:

If we save and publish the post, we should get the message we return from the save function when viewing it on the front end:

The pullquote block rendered on the front end of the website. It says hello from the front end.

Creating a block

Looks like everything is hooked up! We can revert back to what we had in index.js before the test now that we’ve confirmed things are working:

import { registerBlockType } from "@wordpress/blocks";

import metadata from "./block.json";
import Edit from "./edit.js";
import Save from "./save.js";

const { name } = metadata;

registerBlockType(name, {
  edit: Edit,
  save: Save,
});

Notice that the edit and save functions are tied to two existing files in the src directory that @wordpress/create-block generated for us and includes all the additional imports we need in each file. More importantly, though, those files establish the Edit and Save components that contain the block’s markup.

Back end markup (src/edit.js)

import { useBlockProps } from "@wordpress/block-editor";
import { __ } from "@wordpress/i18n";

export default function Edit() {
  return (
    <p {...useBlockProps()}>
      {__("Hello from the Block Editor", "block-example")}
    </p>
  );
}

See what we did there? We’re importing props from the @wordpress/block-editor package which allows us to generate classes we can use later for styling. We’re also importing the __ internationalization function, for dealing with translations.

The pullquote block on the back end, selected and with devtools open beside it displaying the markup.

Front-end markup (src/save.js)

This creates a Save component and we’re going to use pretty much the same thing as src/edit.js with slightly different text:

import { useBlockProps } from "@wordpress/block-editor";
import { __ } from "@wordpress/i18n";

export default function Save() {
  return (
    <p {...useBlockProps.save()}>
      {__("Hello from the front end", "block-example")}
    </p>
  );
}

Again, we get a nice class we can use in our CSS:

The pullquote block on the front end, selected and with devtools open beside it displaying the markup.

Styling blocks

We just covered how to use block props to create classes. You’re reading this article on a site all about CSS, so I feel like I’d be missing something if we didn’t specifically address how to write block styles.

Differentiating front and back-end styles

If you take a look at the block.json in the src directory you’ll find two fields related to styles:

  • editorStyle provides the path to the styles applied to the back end.
  • style is the path for shared styles that are applied to both the front and back end.

Kev Quirk has a detailed article that shows his approach for making the back-end editor look like the front end UI.

Recall that the @wordpress/scripts package copies the block.json file when it processes the code in the /src directory and places compiled assets in the /build directory. It is the build/block.json file that is used to register the block. That means any path that we provide in src/block.json should be written relative to build/block.json.

Using Sass

We could drop a couple of CSS files in the build directory, reference the paths in src/block.json, run the build, and call it a day. But that doesn’t leverage the full might of the @wordpress/scripts compilation process, which is capable of compiling Sass into CSS. Instead, we place our style files in the src directory and import them in JavaScript.

While doing that, we need to be mindful of how @wordpress/scripts processes styles:

  • A file named style.css or style.scss or style.sass, imported into the JavaScript code, is compiled to style-index.css.
  • All other style files are compiled and bundled into index.css.

The @wordpress/scripts package uses webpack for bundling and @wordpress/scripts uses the PostCSS plugin for working for processing styles. PostCSS can be extended with additional plugins. The scripts package uses the ones for Sass, SCSS, and Autoprefixer, all of which are available for use without installing additional packages.

In fact, when you spin up your initial block with @wordpress/create-block, you get a nice head start with SCSS files you can use to hit the ground running:

  • editor.scss contains all the styles that are applied to the back-end editor.
  • style.scss contains all the styles shared by both the front and back end.

Let’s now see this approach in action by writing a little Sass that we’ll compile into the CSS for our block. Even though the examples aren’t going to be very Sass-y, I’m still writing them to the SCSS files to demonstrate the compilation process.

Front and back-end styles

OK, let’s start with styles that are applied to both the front and back end. First, we need to create src/style.scss (it’s already there if you’re using @wordpress/create-block) and make sure we import it, which we can do in index.js:

import "./style.scss";

Open up src/style.scss and drop a few basic styles in there using the class that was generated for us from the block props:

.wp-block-css-tricks-block-example {
  background-color: rebeccapurple;
  border-radius: 4px;
  color: white;
  font-size: 24px;
}

That’s it for now! When we run the build, this gets compiled into build/style.css and is referenced by both the Block Editor and the front end.

Back-end styles

You might need to write styles that are specific to the Block Editor. For that, create src/editor.scss (again, @wordpress/create-block does this for you) and drop some styles in there:

.wp-block-css-tricks-block-example {
  background-color: tomato;
  color: black;
}

Then import it in edit.js, which is the file that contains our Edit component (we can import it anywhere we want, but since these styles are for the editor, it’s more logical to import the component here):

import "./editor.scss";

Now when we run npm run build, the styles are applied to the block in both contexts:

The pullquote block in the WordPress Block Editor with an applied tomoato-colored background.\ behind black text.
The pullquote block ion the front end with an applied rebecca purple-colored background behind black text.

Referencing styles in block.json

We imported the styling files in the edit.js and index.js, but recall that the compilation step generates two CSS files for us in the build directory: index.css and style-index.css respectively. We need to reference these generated files in the block metadata.

Let’s add a couple of statements to the block.json metadata:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 2,
  "name": "css-tricks/block-example",
  "version": "1.0.0",
  "title": "Block Example",
  "category": "text",
  "icon": "format-quote",
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css"
}

Run npm run build once again, install and activate the plugin on your WordPress site, and you’re ready to use it!

You can use npm run start to run your build in watch mode, automatically compiling your code every time you make a change in your code and save.

We’re scratching the surface

Actual blocks make use of the Block Editor’s Settings sidebar and other features WordPress provides to create rich user experiences. Moreover, the fact that there’s essentially two versions of our block — edit and save — you also need to give thought to how you organize your code to avoid code duplication.

But hopefully this helps de-mystify the general process for creating WordPress blocks. This is truly a new era in WordPress development. It’s tough to learn new ways of doing things, but I’m looking forward to seeing how it evolves. Tools like @wordpress/create-block help, but even then it’s nice to know exactly what it’s doing and why.

Are the things we covered here going to change? Most likely! But at least you have a baseline to work from as we keep watching WordPress blocks mature, including best practices for making them.

References

Again, my goal here is to map out an efficient path for getting into block development in this season where things are evolving quickly and WordPress documentation is having a little hard time catching up. Here are some resources I used to pull this together:


Getting Started With WordPress Block Development originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/getting-started-with-wordpress-block-development/feed/ 4 373732
A Deep Introduction to WordPress Block Themes https://css-tricks.com/a-deep-introduction-to-wordpress-block-themes/ https://css-tricks.com/a-deep-introduction-to-wordpress-block-themes/#comments Fri, 04 Feb 2022 15:07:04 +0000 https://css-tricks.com/?p=362799 The relatively new WordPress editor, also known as the WordPress Block Editor, always under development via the Gutenberg plugin, has been with us since 2018. You can use the block editor on any WordPress theme, provided the theme …


A Deep Introduction to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The relatively new WordPress editor, also known as the WordPress Block Editor, always under development via the Gutenberg plugin, has been with us since 2018. You can use the block editor on any WordPress theme, provided the theme loads CSS that the blocks use. But there are new themes that lean into the Block Editor much more deeply.

WordPress Block Themes allow you to build out the entire site using blocks, meaning the theme’s responsibility is mostly design guidelines, and less about controlling the pages and the content on them. This is referred to as Full-Site Editing in WordPress and the themes that are built for this are called Block Themes, because you build out everything with blocks.

Let’s dig into all this.

Illustration of a black vinyl record coming out of a record sleep sleeve from the left that contains a blue tinted image of jazz singer Joséphine Baker's profile looking right with a soft smile and parted lips. The image includes white text that says WordPress 5.9 and code is poetry.
Credit: WordPress.org

Table of Contents

Introduction

Except for those who follow its day-to-day development iterations at GitHub, most development surrounding the block editor is largely visible to users — and that’s not necessarily a bad thing. I have been personally trying to keep myself updated with the block editor through WP Tavern and Gutenberg posts, and have been using both the legacy — or “Classic” editor — as well as the block editor in my personal learning project sites.

After taking a detour to learn and experience headless WordPress sites with Gatsby and Frontity frameworks, I am now back to my native WordPress home.

Though I have been aware of the WordPress theme-experiment GitHub repository for a while — themes made completely out of blocks! — I have only started digging into block themes recently. In fact, I have been using an experimental block-based theme here in this project site.

WordPress 5.9 is now out in the wild and with it comes block-based theming for the masses. This release, dubbed Joséphine, is the formal introduction to WordPress full site editing and Block Themes.

Even though the block-based theming functionality has been available in various iterative forms in previous releases, this is a big deal for the WordPress platform and the ecosystem that relies on it. I’ve had my hands on it and thought I’d share what I’ve learned about block themes in my hands-on experience, as well as some personal thoughts on how it works.

Disclaimer: I am not a block themes expert by any stretch. I am well-versed in WordPress and a major fan of the content management system. My goal here is not to critique WordPress 5.9 or steer you in the direction of whether or not you should like it or want to use it. I’m merely coming from the perspective of an open-minded learner who is building personal sites with fairly deep understanding and familiarity with the WordPress Block Editor.

Before we dive straight into block themes, I think it’s a good idea to form a baseline understanding of just what we’re talking about when we’re tossing around terms, like blocks and site editing, as they’re so incredibly new and game-changing relative to what we’ve known and loved about WordPress for decades.

Block Editor

This is really what we mean any time we refer to the “WordPress Editor.” We call the WordPress Editor the Block Editor because it allows us to create pages and posts where each element— including text, images, videos, headers, footers, etc. — is inserted into the post using blocks that can be arranged modularly to complete page layouts. It evolved from what’s now called the “classic” editor, which was more squarely based on entering content to be published on a page or post in a predefined layout.

A full screenshot of the WordPress Block editor split into three numbers parts that are highlighted in red.
WordPress Block Editor including the block inserter (1), block editor content area (2), and the document and block settings (3)
Credit: WordPress Block Editor Handbook.

It’s sort of like content and layout coming together, where both are managed in the WordPress Editor. So, where we used to rely on the editor for content and (more or less) theme templates to define layout, both are directly editable in the WordPress Editor interface.

You can find more detail here on using the Block Editor.

Block Theme

As explained in the WordPress docs:

A block theme is a WordPress theme with templates entirely composed of blocks so that in addition to the post content of the different post types (pages, posts, …), the block editor can also be used to edit all areas of the site: headers, footers, sidebars, etc.

This WordPress documentation provides an overview of block themes in its knowledgebase, including how to create block themes and styling in this primer.

The bottom line: Block themes are different than “classic” WordPress themes. Rather than relying strictly on PHP files that conform to the WordPress Template Hierarchy, a WordPress Block Theme consists of block-based HTML templates — assembled groups of blocks that can be styled and arranged in the WordPress Site Editor (that’s coming up next) as well as using a theme.json file for global styling tokens.

Site Editor

This is the crown jewel of WordPress 5.9. While it is officially called the WordPress Site Editor, it’s largely been referred to as Full-Site Editing** (FSE) during development and is described as “the cohesive experience that allows you to directly edit and navigate between various templates, template parts, styling options, and more.” Phew, that’s a lot!

Credit: WordPress Support

The WordPress Site Editor allows us to create and editing templates that are made of blocks. So the idea is that we can assemble a group of blocks that can be applied globally to a site. Like a header component, for example. That might consist of blocks for a site logo, a primary menu, and a tagline. The site editor allows us to create a new block theme or modify an existing theme to give the site’s global appearance a completely new look without writing a line of code.

So, you know how you’ve had to build a theme in the past with a bunch of PHP templates? That’s no longer the case. Theme “development” now has a UI that’s available directly in WordPress.

More detail on using site editor is in the WordPress documentation.

The official WordPress Glossary has additional terms and definitions you may want to explore as we dig deeper into WordPress Block Themes and FSE.

Using the block editor with classic themes

The WordPress Block Editor can be used in both the classic and block themes. When the Gutenberg editor project began, the classic TinyMCE-based editor was detached from WordPress Core into the Classic Editor plugin. As long as the Classic Editor plugin is installed and active, content writing is pretty normal as it was before blocks were introduced.

Prior to the formal introduction of block editor features, we had to install the experimental Gutenberg plugin. By simply switching plugins, individual page or post contents could be created with either editor. The WordPress 5.0 release introduced the block editor alongside the default Twenty Nineteen theme, demonstrating how to add block editor features and explore its power.

In other words, the evolution toward FSE has been building for a while. And because of that, we get to enjoy a high level of backwards compatibility that allows us all to adopt block-based features when we’re good and ready.

The anatomy of block-based themes

Experimental block-based themes have been in development since early 2020. At the time I’m writing this, the GitHub theme experiment repository lists 12 block themes that explore some aspect of creating themes using blocks or block templates.

But it was probably the Twenty Twenty-One theme that was the first “default” theme to make blocks a first-class citizen, introducing block styles and block patterns, though the recently updated versions of Twenty Nineteen, and Twenty Twenty also include bundled block styling and block patterns. Currently, there are more than 130 themes from the community with bundled block editor patterns, block styles feature, including my favorite, Anders Noren’s Eksell theme.

With the ongoing development of the WordPress Block Editor’s FSE features, even more block-based themes are also being introduced.

So, what does the development of block-based themes mean for those of us who are deeply entrenched in the “classic” way of building WordPress themes? That’s what I want to look at in this section.

The file structure of block themes

In classic PHP-powered theming, markup elements are generated with PHP and JavaScript, while in block themes those templates are entirely composed of HTML blocks and structural CSS provided by the block editor. This might sound scary for lots of folks, but it’s easy to imagine just how liberating it is for others as it lowers the bar when it comes to developing a WordPress theme.

The structure of a block theme is drastically different from the classic WordPress Template Hierarchy that we all are used to. In classic PHP-based themes, page element markup has to be generated with PHP and JavaScript, whereas in block themes, the WordPress Core provides both the markup and basic styling. For example, the default Twenty Twenty-One theme contains 48 PHP files and 11 JavaScript files weighing in at 4.5 MB. Its block-based sibling, the experimental TT1 Blocks theme, contains only four PHP files, one JavaScript file, and eight HTML files at 3.5 MB.

Screenshot of a Mac window open to the default Twenty Twenty-One WordPress theme, displaying a long list of files.
Twenty Twenty-One theme folder
Screenshot of a Mac window open to the TT1 theme folder, showing that WordPress Block Themes contain fewer files.
TT1 theme folder

A block theme structure can be very simple with just a few required files : index.php, style.css and template/index.html. The following is a typical block theme file structure, pulled from the WordPress Editor Handbook:

#! basic block-theme structure
theme
|__ style.css
|__ functions.php
|__ index.php
|__ theme.json
|__ templates
    |__ index.html
    |__ single.html
    |__ archive.html
    |__ ...
|__ parts
    |__ header.html
    |__ footer.html
    |__ sidebar.html
    |__ ...
  • styles.css: Contains theme’s style sheet
  • functions.php: Contains theme setup and may include additional files, enable an editor style sheet, and enqueue style.css, if there are any
  • index.php: An empty file to switch to default file in case the block theme is activated without the WordPress Block Editor.
  • theme.json: Optional configuration file used to enable or disable features and set default styles for both the website and blocks
  • templates: Contains page templates that are composed of blocks. These files follow the same template hierarchy as traditional themes.
    • index.html: The primary template to generate a post or page, analogous to index.php in classic themes
    • single.html: The template to generate single posts or pages
    • archive.html: The template to generate archive lists of posts
  • parts: The common collections of blocks to be used in block templates
    • header.html: The global header block
    • footer.html: The global footer block
    • sidebar.html: An optional global sidebar block

A list of theme blocks including that are specific to block themes is available in WordPress Block Editor Handbook.

Templates and template parts

Templates are basically group of assembled blocks that might include reusable block parts, like a site header or footer. Different blocks are used to compose a page template. For example, that might be a list of blog posts, a list of products, or even a widget.

Here’s an example of a block template pulled from the WordPress Block Editor Handbook.


<!-- wp:site-title /-->

<!-- wp:image {"sizeSlug":"large"} -->
<figure class="wp-block-image size-large">
    <img src="https://cldup.com/0BNcqkoMdq.jpg" alt="" />
</figure>
<!-- /wp:image -->

<!-- wp:group -->
<div class="wp-block-group">
    <!-- wp:post-title /-->
    <!-- wp:post-content /-->
</div>
<!-- /wp:group -->

<!-- wp:group -->
<div class="wp-block-group">
    <!-- wp:heading -->
    <h2>Footer</h2>
    <!-- /wp:heading -->
</div>
<!-- /wp:group -->

Creating WordPress Block Themes

The WordPress Site Editor is now the primary tool for defining the look and feel of a WordPress website. You may be used to using the WordPress Customizer to do these things — and some themes have heavily tapped into that to do what the site editor is now designed to do.

So, no longer is the block editor for pages and posts; it’s the way WordPress themes are created.

I’m assuming that many of you have already used the block editor, and don’t really need a deep lesson on what it is or how to use it. That said, it’s worth poking at it a bit since it’s the impetus for everything related to WordPress theming moving forward, now that WordPress 5.9 is in the wild.

In fact, when we talk about block editing and theming, yes, we’re talking about the block editor. But really what we’re talking about is the WordPress Site Editor.

The WordPress Site Editor interface

Even as an early adopter of the Gutenberg plugin, I find the experience of the site editor intimidating and frustrating. It changes frequently and often drastically with each new release. I am hopeful, though, that WordPress 5.9 is a sort of line in the sand that helps stabilize that rocky feeling.

The site editor is accessed the same way you’re already used to accessing the WordPress Customizer. It’s located under Appearance in the dashboard menu, called Editor.

Screenshots of the WordPress admin Themes screen side-by-side, the first showing the classic WordPress menu items like Customize, Widgets, and Menus, while the second shows how a WordPress Block Themes only displays a single Editor menu item.
The site editor option is available only when a block theme is activated. If you’re using a classic theme, you’ll get the classic UI to go with it.

Let’s briefly walk-through the new Editor interface.

First, navigate to the site editor by clicking Appearance → Editor from the WordPress admin menu. That menu item may have a red “beta” label on it, like it currently does in WordPress 5.9.

That takes you to the site editor, which displays either your homepage or post archive, depending on what you have your homepage set to in Settings → Reading. From there it sort of looks like the fullscreen version of the block editor when creating or editing a page or post. But click on the WordPress logo in the top-left of the screen, and a left panel opens up revealing the WordPress Site Editor and its menu to navigate between different parts of the site. This includes Site, Templates, and Template Parts.

Screenshot of the WordPress Site Editor. There is a dark gray left panel open with an Editor heading and three links for Site, Templates, and Template Parts. The main content shows a preview of the site homepage in the WordPress Block Editor.

Let’s click into Templates. This shows us a list of the available templates provided by the theme, complete with a description of each one and where it is registered (e.g. the parent or a child theme).

Screenshot of the site editor's Templates screen which shows a two-column table listing template on the left and who a template was added by on the right.

The other way to get to this screen is from the initial page we landed on when entering the site editor. Click the name of the template in the top admin bar to reveal a button that takes you directly to the same Templates screen.

Screenshot of the Home template open in the WordPress Site Editor. The template name is at the top of the screen in a white toolbar and is expanded with a submenu that describes the template and provides a black button with white text to view all templates.

Any of templates can be edited just like any page or post in the block editor. Let’s say I don’t like to have a featured image on my index page and want to remove it. Simply delete the featured image block and save the template.

The other key part of the site editor UI is a list view that outlines the current blocks that are placed in the template. This has been a feature in WordPress since the introduction of the block editor, but what’s new this time around is that you can open and close parent blocks that contain child blocks like an accordion. Not only that, but it supports dragging and dropping blocks to change the layout directly from there.

The WordPress Site Editor with a white left panel expanded revealing an outline of the current blocks that are applied to the template.

One more thing in the site editor UI: you can clear out customizations with the click of a button. From the Templates screen, click the kebob menu next to a template and select the option to Clear customizations. This is a nice way to reset and start from scratch, should you need to.

Screenshot of the Template Parts screen in the WordPress Site Editor, showing a two-column able with a column that displays template names, and a column that identifies the location of the template part.

The WordPress Core team publishes regular updates on what’s new at Make WordPress Core. It’s worth bookmarking that to stay posted on the latest changes to the WordPress Block Editor and Site Editor.

Creating Templates and Template Parts

Templates, as you know, are core to WordPress theming. They enforce consistent and reusable layouts. That doesn’t change in WordPress 5.9. And neither does the fact that we can create template parts that are like module pieces that can be used in multiple template, say a post query that lives in an archive template and the home template.

What’s different in WordPress 5.9 is that they are created and managed with the site editor rather than PHP files that live in the theme folder.

The Block Editor Handbook lists three ways to create templates and template parts: (a) manually, by creating HTML files containing block markup, (b) using the site editor, and (c) using the template editing mode in the block editor.

Brief descriptions of creating template in the site editor and template editing mode are available in the Block Theme handbook. The WordPress 5.9 allows to create a new template using editor mode.

Screenshot of the Template Parts screen open in the WordPress Site Editor. A modal is open above the UI that contains an interface to create a template part, including the part's name and area.

The customized templates can then be exported to include in a block theme. So, yeah, we now have the ability to create a fully functioning WordPress theme without writing a line of code! The exported folder currently does not contain theme.json file, however there is a proposal over at GitHub to allow exporting both block themes and styles.

Screenshot of the WordPress Site Editor preferences panel open as a white panel to the left of the screen.

But for those who prefer working more closely with code, then manually creating WordPress templates and template parts is still a thing. You can still crack open a code editor and create HTML files containing block markup.

Global settings and styles (theme.json)

In classic themes, we write the styling rules in a style.css file. In block themes, styling is more challenging because CSS comes from different sources (e.g. core blocks, themes, and users). WordPress 5.8 introduced a concept of Global Styles — which is essentially a theme.json file — that, according to the docs, consolidate “the various APIs related to styles into a single point – a theme.json file that should be located inside the root of the theme directory.“

Screenshot of a theme dot jayson file open in the VS Code editor. The file contains objects for version and settings. The settings object contains a color object. The color object contains a palette objects which contains properties for slightly, color, name, and default.

The theme.json file is said to have been designed to offer more granular styling structure for theme authors with options to manage and customize the CSS styles coming from various origins. For example, a theme author may set certain styling features that are hidden from users, define default colors, font sizes and other features available to the user, and may set the default layout of the editor as well. Plus, theme.json allows you to customize styling on a per-block basis. It’s powerful, flexible, and super maintainable!

The block editor is expected to provide all the basic styling that theme authors are allowed to customize style, as defined by the theme.json file. However, the theme.json file could get quite long for a complex theme, and currently there is no way to partition it in a more digestible way. There is a GitHub ticket to restructure it so that different theme.json files map to a /styles folder. That would be a nice enhancement for developer experience.

The default Twenty Twenty-Two theme is a good example of how WordPress full-site editing features use theme.json for global settings and styling blocks.

WordPress Block Theme approaches

Maybe you’ve always made WordPress themes from scratch. Perhaps you’ve relied on the Underscores theme as a starting point. Or maybe you have a favorite theme you extend with a child theme. The new features of the WordPress Site Editor really change the way we make themes.

Following are a few emerging strategies for block-based theme development that are deeply integrated with the WordPress Site Editor.

Universal themes

The Automattic team has built a Blockbase universal theme that’s dubbed as a new way to build themes, sort of similar to the Underscores starter theme. The Blockbase theme provides temporary “ponyfill” styles that the block editor “does not yet take into account on theme.json ‘custom’ properties” and that may eventually become obsolete once the Gutenberg plugin fully matures and is integrated into WordPress Core.

Using the universal parent theme approach, the Automattic has already released eight Blockbase child themes, and several others are in progress over at GitHub.

Twenty Twenty-Two default theme

The Twenty Twenty-Two default theme is another excellent starting point, as it’s really the first WordPress theme that ships with WordPress that is designed to work with the site editor.

In my opinion, this theme is excellent for theme developers who are already familiar with FSE features to showcase what is possible. For others users who are not developers and are not familiar with FSE features, customizations it in the block editor, then exporting it as a child theme could be painfully frustrating and overwhelming.

Hybrid themes

The concept of “Hybrid” themes in the context of FSE is discussed in this GitHub ticket. The idea is to provide paths for any user to use the site or template editor to override traditional theme templates.

Justin Tadlock in this WP Tavern post predicts four types of themes — block only, universal, hybrid, and classic — and speculates that theme authors may split between “block themes and a mashup of classic/hybrid themes.”

Proof in the pudding is provided by Frank Klein in “What I Learned Building a Hybrid Theme”:

A hybrid theme mixes the traditional theming approach with full-site editing features. A key component here is the theme.json file. It offers more control over the block editor’s settings, and simplifies styling blocks. A hybrid theme can use block templates as well, but that’s optional.

Frank is the author of the Block-Based Bosco theme and has expanded further on what a “hybrid theme” is by creating a hybrid version of the default Twenty Twenty theme. The theme is available on GitHub. Currently, there are no hybrid themes in the WordPres Theme Directory.

WordPress community themes

At the time of writing, there are 47 block-based themes with FSE features available in the theme directory. As expected, this approach is widely varied.

For example, in this post, Aino block theme author Ellen Bower discusses how they converted their classic theme into a block theme, detailing what makes a theme a “block” theme. The file structure of this approach looks different from the standard block theme structure we covered earlier.

Another popular block theme, Tove by Andars Noren, is described as a flexible base theme that follows the standard block theme file structure.

There’s also a very simple single page proof of the concept theme by Carolina Nymark that contains nothing but a single index.html called Miniblock OOAK. It’s already available in the theme directory, as is another one from Justin Tadlock that’s a work in progress (and he wrote up his process in a separate article).

Block Theme Generator app

Even though we’ve already established how friendly WordPress Block Themes are for non-developers, there are tools that help create complete block themes or merely a customized theme.json file.

David Gwyer, an Automattic engineer, has been working on a Block theme generator app which, at the time of writing, is in beta and available for testing by request.

Screenshot of the Block Theme Generator app homepage. It has a bright blue background and dark blue text that welcomes you to the site, and a screenshot of the app.

In my brief testing, the app only allowed me to generate customized theme.json file. But Gwyer told to WP Tavern that the app isn’t fully baked just yet, but features are being added often. Once complete, this could be a very helpful resource for theme authors to create customized block themes.

Block themes that are currently in use

This Twitter thread from Carolina Nymark shows some examples of block themes that are live and in production at the time of this writing. In a recent Yoast article, Carolina listed a bunch of personal and business websites that use block themes.

Personal sites

Business sites

As I mentioned earlier, I also have been using a block theme for one of my personal websites for a while. The default Twenty Twenty-Two theme also currently shows more than 60,000 active installs, which tells me there are many more examples of block-based theme implementations in the wild.

Building Block Child Themes

Child theming is still a thing in this new era of WordPress blocks, though something that’s still in early days. In other words, there is no clear approach to do make a block-based child theme, and there are no existing tools to help at the moment.

That said, a few approaches for creating WordPress child block themes are emerging.

Create Blockbase Theme plugin

The Automattic team is working on a plugin called Create Blockbase Theme. This will make it fairly trivial to create child themes based on the Blockbase universal theme we talked about earlier. Ben Dwyer has discussed how theme authors can build Blockbase child themes with simple six steps and without writing a line of code.

I tested the plugin in my own local environment, making only small changes to my Blockbase theme install, and everything appeared to work. Just note that the plugin is still experimental and under development, though you can follow the roadmap to see what’s up.

Using an alternate theme.json file

Kjell Reigstad, author of the default WordPress Twenty Twenty-Two theme, demonstrates how swapping a single theme.json file with another theme.json file that contains different style configurations can change the look and feel of a block-based theme design.

Kjell has opened a pull request that shows off several experimental child themes that are available for testing at the GitHub theme-experiment GitHub repository.

A three-by-two grid of screenshots of child themes based on the default WordPress Twenty Twenty-Two theme in alternate colors schemes.

Along these same lines, Ryan Welcher is in the process of developing a theme.json builder tool that will generate a customized theme.json file to facilitate non-coders to create similar child themes. More can be found in this WP Tavern post.

The Framboise child theme (available in theme directory) is an early example of that approach which includes only a single theme.json file.

Is there even a need for child themes?

Rich Tabor asks the question:

Indeed, a single theme.json file could serve as a child theme on its own. There is an ongoing discussion about allowing theme authors to ship multiple theme.json files with block themes that offer multiple global style variations. This way, a WordPress user could pick one of the variations to use on the site.

Some features of global style variations are already included in Gutenberg v12. 5 and expected to be available with WordPress 6.0.

Some personal thoughts

I’d be remiss to end this without weighing in on all this from a personal level. I’ll do this briefly in a few points.

Block themes are a WordPress answer to Jamstack criticisms

Jamstack enthusiasts have lobbed criticisms at the WordPress platform, most notably that WordPress themes are bloated with PHP files. Well, that’s no longer the case with WordPress Block Themes.

We saw earlier how an entire theme can be a single index.html file and a theme.json file. No bloat there. And nothing but markup.

I miss the WordPress Customizer

Especially the ability to inject custom code. From here on out, it’s going to require a deep level of familiarity with the WordPress Site Editor UI to accomplish the same thing.

Customizations a site is easy-peasy.

Customizing a classic theme — even something as minimal as changing fonts — can be difficult if you don’t know what you’re doing. That’s changed now with the site editor and the introduction of the theme.json file, where a theme can be customized (and even exported!) without writing a single line of code.

I still hold my opinion, though that the site editor interface is confusing. I think a pleasant user experience is a far ways off but looking forward to the next WordPress 6.0 release for better user experience.

Barriers to designing themes is getting lower.

It’s less about PHP and template files, and more about developing patterns and creating content. That sounds exactly what a content management system should be designed to do! I am already excited with new features being considered for the WordPress 6.0 release.

Resources

There is already a ton of other articles that cover WordPress Block Themes, full-site editing, and the block editor. And many of those came before WordPress 5.9 was released!

So, in addition to this article, here’s a collection of others for you to consider as you begin or continue down your journey of WordPress blocks and site editing.

WordPress 5.9

Site editor and block themes

Selected blog posts


As expected in beta testing, the site editor is still intimating and confusing, nevertheless, I am finding it a fun to work with block themes. Indeed, I have been already modifying Twenty Twenty-Two as a child theme and plan to create style alternatives using single theme.json file.

Have you been using block themes in your project, if so, share your experience and thoughts; I love reading any comments and feedback!


A Deep Introduction to WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/a-deep-introduction-to-wordpress-block-themes/feed/ 9 362799