WordPress – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Mon, 06 Mar 2023 15:26:41 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 WordPress – CSS-Tricks https://css-tricks.com 32 32 45537868 Managing Fonts in WordPress Block Themes https://css-tricks.com/managing-fonts-in-wordpress-block-themes/ https://css-tricks.com/managing-fonts-in-wordpress-block-themes/#respond Mon, 06 Mar 2023 15:26:31 +0000 https://css-tricks.com/?p=377123 Fonts are a defining characteristic of the design of any site. That includes WordPress themes, where it’s common for theme developers to integrate a service like Google Fonts into the WordPress Customizer settings for a “classic” PHP-based theme. That hasn’t …


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

]]>
Fonts are a defining characteristic of the design of any site. That includes WordPress themes, where it’s common for theme developers to integrate a service like Google Fonts into the WordPress Customizer settings for a “classic” PHP-based theme. That hasn’t quite been the case for WordPress block themes. While integrating Google Fonts into classic themes is well-documented, there’s nothing currently available for block themes in the WordPress Theme Handbook.

That’s what we’re going to look at in this article. Block themes can indeed use Google Fonts, but the process for registering them is way different than what you might have done before in classic themes.

What we already know

As I said, there’s little for us to go on as far as getting started. The Twenty Twenty-Two theme is the first block-based default WordPress theme, and it demonstrates how we can use downloaded font files as assets in the theme. But it’s pretty unwieldy because it involves a couple of steps: (1) register the files in the functions.php file and (2) define the bundled fonts in the theme.json file.

Since Twenty Twenty-Two was released, though, the process has gotten simpler. Bundled fonts can now be defined without registering them, as shown in the Twenty Twenty-Three theme. However, the process still requires us to manually download font files and bundle them into the themes. That’s a hindrance that sort of defeats the purpose of simple, drop-in, hosted fonts that are served on a speedy CDN.

What’s new

If you didn’t already know, the Gutenberg project is an experimental plugin where features being developed for the WordPress Block and Site Editor are available for early use and testing. In a recent Theme Shaper article, Gutenberg project lead architect Matias Ventura discusses how Google Fonts — or any other downloaded fonts, for that matter — can be added to block themes using the Create Block Theme plugin.

This short video at Learn WordPress provides a good overview of the Create Block Theme plugin and how it works. But the bottom line is that it does what it says on the tin: it creates block themes. But it does it by providing controls in the WordPress UI that allow you to create an entire theme, child theme, or a theme style variation without writing any code or ever having to touch template files.

I’ve given it a try! And since Create Block Theme is authored and maintained by the WordPress.org team, I’d say it’s the best direction we have for integrating Google Fonts into a theme. That said, it’s definitely worth noting that the plugin is in active development. That means things could change pretty quickly.

Before I get to how it all works, let’s first briefly refresh ourselves with the “traditional” process for adding Google Fonts to classic WordPress themes.

How it used to be done

This ThemeShaper article from 2014 provides an excellent example of how we used to do this in classic PHP themes, as is this newer Cloudways article by Ibad Ur Rehman.

To refresh our memory, here is an example from the default Twenty Seventeen theme showing how Google fonts are enqueued in the functions.php file.

function twentyseventeen_fonts_url() {
  $fonts_url = '';
  /**
   * Translators: If there are characters in your language that are not
   * supported by Libre Franklin, translate this to 'off'. Do not translate
   * into your own language.
   */
  $libre_franklin = _x( 'on', 'libre_franklin font: on or off', 'twentyseventeen' );
  if ( 'off' !== $libre_franklin ) {
    $font_families = array();
    $font_families[] = 'Libre Franklin:300,300i,400,400i,600,600i,800,800i';
    $query_args = array(
      'family' => urlencode( implode( '|', $font_families ) ),
      'subset' => urlencode( 'latin,latin-ext' ),
    );
    $fonts_url = add_query_arg( $query_args, 'https://fonts.googleapis.com/css' );
  }
  return esc_url_raw( $fonts_url );
}

Then Google Fonts is pre-connected to the theme like this:

function twentyseventeen_resource_hints( $urls, $relation_type ) {
  if ( wp_style_is( 'twentyseventeen-fonts', 'queue' ) && 'preconnect' === $relation_type ) {
    $urls[] = array(
      'href' => 'https://fonts.gstatic.com',
      'crossorigin',
    );
  }
  return $urls;
}
add_filter( 'wp_resource_hints', 'twentyseventeen_resource_hints', 10, 2 );

What’s wrong with the traditional way

Great, right? There’s a hitch, however. In January 2022, a German regional court imposed a fine on a website owner for violating Europe’s GDPR requirements. The issue? Enqueuing Google Fonts on the site exposed a visitor’s IP address, jeopardizing user privacy. CSS-Tricks covered this a while back.

The Create Block Theme plugin satisfies GDPR privacy requirements, as it leverages the Google Fonts API to serve solely as a proxy for the local vendor. The fonts are served to the user on the same website rather than on Google’s servers, protecting privacy. WP Tavern discusses the German court ruling and includes links to guides for self-hosting Google Fonts.

How to use Google Fonts with block themes

This brings us to today’s “modern” way of using Google Fonts with WordPress block themes. First, let’s set up a local test site. I use Flywheel’s Local app for local development. You can use that or whatever you prefer, then use the Theme Test Data plugin by the WordPress Themes Team to work with dummy content. And, of course, you’ll want the Create Block Theme plugin in there as well.

Have you installed and activated those plugins? If so, navigate to AppearanceManage theme fonts from the WordPress admin menu.

Manage Theme Fonts screen with type samples for Space Mono.
Source: WordPress Theme Directory

The “Manage theme fonts” screen displays a list of any fonts already defined in the theme’s theme.json file. There are also two options at the top of the screen:

  • Add Google fonts. This option adds Google Fonts directly to the theme from the Google fonts API.
  • Add local fonts. This option adds downloaded font files to the theme.

I’m using a completely blank theme by WordPress called Emptytheme. You’re welcome to roll along with your own theme, but I wanted to call out that I’ve renamed Emptytheme to “EMPTY-BLANK” and modified it, so there are no predefined fonts and styles at all.

Themes screen showing Empty Theme as the active selection with no screenshot preview.

I thought I’d share a screenshot of my theme’s file structure and theme.json file to show that there are literally no styles or configurations going on.

VS Code file explorer on the left and an open theme.json file on the right.
File structure of Emptytheme (left) and theme.json file (right)

Let’s click the “Add Google Fonts” button. It takes us to a new page with options to choose any available font from the current Google Fonts API.

Add Google Fonts to your theme screen with the select font menu open showing a list of available fonts.

For this demo, I selected Inter from the menu of options and selected the 300, Regular, and 900 weights from the preview screen:

Add Google Fonts to your theme screen with Inter selected and type samples below it of the various weight variations.

Once I’ve saved my selections, the Inter font styles I selected are automatically downloaded and stored in the theme’s assets/fonts folder:

VS Code file explorer on the left showing Inter font files; theme.json on the right showing Inter references.

Notice, too, how those selections have been automatically written to the theme.json file in that screenshot. The Create Block Theme plugin even adds the path to the font files.

View the entire theme.json code
{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "layout": {
      "contentSize": "840px",
      "wideSize": "1100px"
    },
    "typography": {
      "fontFamilies": [
        {
          "fontFamily": "Inter",
          "slug": "inter",
          "fontFace": [
            {
              "fontFamily": "Inter",
              "fontStyle": "normal",
              "fontWeight": "300",
              "src": [
                "file:./assets/fonts/inter_300.ttf"
              ]
            },
            {
              "fontFamily": "Inter",
              "fontStyle": "normal",
              "fontWeight": "900",
              "src": [
                "file:./assets/fonts/inter_900.ttf"
              ]
            },
            {
              "fontFamily": "Inter",
              "fontStyle": "normal",
              "fontWeight": "400",
              "src": [
                "file:./assets/fonts/inter_regular.ttf"
              ]
            }
          ]
        }
      ]
    }
  }
}

If we go to the Create Block Theme’s main screen and click the Manage theme fonts button again, we will see Inter’s 300, 400 (Regular), and 900 weight variants displayed in the preview panel.

Manage Theme Fonts screen with a button to Add Google Font highlighted in red.

A demo text preview box at the top even allows you to preview the selected fonts within the sentence, header, and paragraph with the font size selection slider. You can check out this new feature in action in this GitHub video.

The selected font(s) are also available in the Site Editor Global Styles (AppearanceEditor), specifically in the Design panel.

Wordpress Site Editor screen with navigation panel open and highlighting the Edit button.

From here, navigate to TemplatesIndex and click the blue Edit button to edit the index.html template. We want to open the Global Styles settings, which are represented as a contrast icon located at the top-right of the screen. When we click the Text settings and open the Font menu in the Typography section… we see Inter!

Open template file in the Site Editor with an arrow pointing out the Global Styles settings button.

Same thing, but with local fonts

We may as well look at adding local fonts to a theme since the Create Block Theme plugin provides that option. The benefit is that you can use any font file you want from whatever font service you prefer.

Without the plugin, we’d have to grab our font files, drop them somewhere in the theme folder, then resort to the traditional PHP route of enqueuing them in the functions.php file. But we can let WordPress carry that burden for us by uploading the font file on the Add local fonts screen using the Create Block Theme interface. Once a file is selected to upload, font face definitions boxes are filled automatically.

Add local fonts to your theme screen with options to upload a font file and set its name, style, and weight.

Even though we can use any .ttf, .woff, or .woff2 file, I simply downloaded Open Sans font files from Google Fonts for this exercise. I snatched two weight variations, regular and 800.

The same auto-magical file management and theme.json update we saw with the Google Fonts option happens once again when we upload the font files (which are done one at a time). Check out where the fonts landed in my theme folder and how they are added to theme.json:

VS Code showing the font files and the theme.json file references to the font.

Removing fonts

The plugin also allows us to remove font files from a block theme from the WordPress admin. Let’s delete one of the Open Sans variants we installed in the last section to see how that works.

The interface for removing a font from the theme.

Clicking the Remove links triggers a warning for you to confirm the deletion. We’ll click OK to continue.

Modal confirming the font deletion.

Let’s open our theme folder and check the theme.json file. Sure enough, the Open Sans 800 file we deleted on the plugin screen removed the font file from the theme folder, and the reference to it is long gone in theme.json.

Updated theme.json file showing the font references have been removed.

There’s ongoing work happening

There’s talk going on adding this “Font Manager” feature to WordPress Core rather than needing a separate plugin.

An initial iteration of the feature is available in the repo, and it uses the exact same approach we used in this article. It should be GDPR-compliant, too. The feature is scheduled to land with WordPress 6.3 release later this year.

Wrapping up

The Create Block Theme plugin significantly enhances the user experience when it comes to handling fonts in WordPress block themes. The plugin allows us to add or delete any fonts while respecting GDPR requirements.

We saw how selecting a Google Font or uploading a local font file automatically places the font in the theme folder and registers it in the theme.json file. We also saw how the font is an available option in the Global Styles settings in the Site Editor. And if we need to remove a font? The plugin totally takes care of that as well — without touching theme files or code.

Thanks for reading! If you have any comments or suggestions, share them in the comments. I’d love to know what you think of this possible direction for font management in WordPress.

Additional resources

I relied on a lot of research to write this article and thought I’d share the articles and resources I used to provide you with additional context.

WordPress font management

GitHub issues

European GDPR requirements


Managing Fonts 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/managing-fonts-in-wordpress-block-themes/feed/ 0 377123
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
WordPress Playground: Running WordPress in the Browser https://css-tricks.com/wordpress-playground-run-in-browser/ https://css-tricks.com/wordpress-playground-run-in-browser/#comments Mon, 19 Dec 2022 16:52:45 +0000 https://css-tricks.com/?p=376297 Being able to quickly spin up a WordPress instance has been the strength of WordPress ever since its famous “five-minute install”. Upload a few files, configure a few settings, and you’re off.

The friction of uploading files has gotten …


WordPress Playground: Running WordPress in the Browser originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Being able to quickly spin up a WordPress instance has been the strength of WordPress ever since its famous “five-minute install”. Upload a few files, configure a few settings, and you’re off.

The friction of uploading files has gotten a lot easier, thanks to plenty of “one-click” install options many hosts offer (including DigitalOcean and Cloudways).

Some companies have tried to abstract the process even more, using the multi-site features of WordPress to fire up disposable instances for testing and demos. WordPress Sandbox and WP Sandbox come to mind. Scaling can be an issue here, as instances run on the same install adding lag to the entire network. I worked on a headless WordPress project that did this in the background for users, and I recall the incredibly long wait it would take users to create a new account as the number of sites in the network piled up.

Enter WordPress Playground. It runs entirely in the browser which is mindblowing to me as a long-time WordPress user. If you’re having a hard time wrapping your head around how it all works like I did, that link to the overview spells it out nicely:

Dang, that’s cool. The move to SQLite is especially interesting, as it could bring huge performance gains to many sites that might not need the full heft of WordPress — a “WordPress Lite” as Chris recently described it in a different context. In fact, that work is already happening in the experimental WordPress performance plugin.

The evolution to a light, frictionless WordPress is a fun space to watch. I imagine there’s a good chunk of existing WordPress sites that stand to benefit from a slimmed-down CMS. The demo offers a glimpse at what an onboarding experience for that sort of thing could look like.

WordPress Playground startup showing theme and plugin options.
Select a theme, choose your features, and go!

WordPress Playground: Running WordPress in the Browser originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/wordpress-playground-run-in-browser/feed/ 1 376297
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
Using The New Constrained Layout In WordPress Block Themes https://css-tricks.com/using-the-new-constrained-layout-in-wordpress-block-themes/ https://css-tricks.com/using-the-new-constrained-layout-in-wordpress-block-themes/#comments Wed, 30 Nov 2022 14:11:10 +0000 https://css-tricks.com/?p=375278 One of the main goals of the WordPress Site Editor (and, yes, that is now the “official” name) is to move basic block styling from CSS to structured JSON. JSON files are machine-readable, which makes it consumable by …


Using The New Constrained Layout In WordPress Block Themes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
One of the main goals of the WordPress Site Editor (and, yes, that is now the “official” name) is to move basic block styling from CSS to structured JSON. JSON files are machine-readable, which makes it consumable by the JavaScript-based Site Editor for configuring a theme’s global styles directly in WordPress.

It’s not all the way there yet! If we look at the Twenty Twenty-Two (TT2) default theme, there were two main unresolved issues: styling interactions (like :hover, :active, :focus), and the margins and padding of layout containers. You can see how those were temporarily fixed in the TT2 style.css file rather than making it into the theme.json file.

WordPress 6.1 fixed those issues and what I want to do is look specifically at the latter. Now that we have JSON-ified styles for the margins and padding of layout containers, that opens us up to more flexible and robust ways to define spacing in our theme layouts.

What kind of spacing are we talking about?

First off, we already have root-level padding which is a fancy way of describing padding on the <body> element. That’s nice because it ensures consistent spacing on an element that is shared on all pages and posts.

But there’s more to it because now we have a way for blocks to bypass that padding and align themselves full-width. That’s thanks to padding-aware alignments which is a new opt-in feature in theme.json. So, even if you have root-level padding, you can still allow, say, an image (or some other block) to break out and go full-width.

That gets us to another thing we get: constrained layouts. The idea here is that any blocks nested in the layout respect the layout’s content width — which is a global setting — and do not flow outside of it. We can override that behavior on a block-by-block basis with alignments, but we’ll get to that.

Let’s start with…

Root-level padding

Again, this isn’t new. We’ve had the ability to set padding on the <body> element in theme.json since the experimental Gutenberg plugin introduced it in version 11.7. We set it on the styles.spacing object, where we have margin and padding objects to define the top, right, bottom, and left spacing on the body:

{
  "version": 2,
  "styles": {
    "spacing": {
      "margin": {
        "top": "60px",
        "right": "30px",
        "bottom": "60px",
        "left": "30px"
      },
      "padding": {
        "top": "30px",
        "right": "30px",
        "bottom": "30px",
        "left": "30px"
      }
    }
  }
}

This is a global setting. So, if we were to crack open DevTools and inspect the <body> element, we would see these CSS styles:

body {
  margin-top: 60px;
  margin-right: 30px;
  margin-bottom: 60px;
  margin-left: 30px;
  padding-top: 30px;
  padding-right: 30px;
  padding-bottom: 30px;
  padding-left: 30px;
}

Cool. But herein lies the issue of how in the world we can allow some blocks to break out of that spacing to fill the full screen, edge-to-edge. That’s why the spacing is there, right? It helps prevent that from happening!

But there are indeed plenty of cases where you might want to break out of that spacing on a one-off instance when working in the Block Editor. Say we plop an Image block on a page and we want it to go full-width while the rest of the content respects the root-level padding?

Enter…

Padding-aware alignments

While attempting to create the first default WordPress theme that defines all styles in the theme.json file, lead designer Kjell Reigstad illustrates the challenging aspects of breaking out of root-level padding in this GitHub issue.

Root-level padding prevents blocks from taking up the full viewport width (left). But with padding-aware alignments, some blocks can “opt-out” of that spacing and take up the full viewport width (right). (Image credit: Kjell Reigstad)

New features in WordPress 6.1 were created to address this issue. Let’s dig into those next.

useRootPaddingAwareAlignments

A new useRootPaddingAwareAlignments property was created to address the problem. It was actually first introduced in the Gutenberg plugin v13.8. The original pull request is a nice primer on how it works.

{
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "useRootPaddingAwareAlignments": true,
    // etc.
  },

Right off the bat, notice that this is a feature we have to opt into. The property is set to false by default and we have to explicitly set it to true in order to enable it. Also notice that we have appearanceTools set to true as well. That opts us into UI controls in the Site Editor for styling borders, link colors, typography, and, yes, spacing which includes margin and padding.

Setting appearanceTools set to true automatically opts blocks into margin and padding without having to set either settings.spacing.padding or setting.spacing.margin to true.

When we do enable useRootPaddingAwareAlignments, we are provided with custom properties with root padding values that are set on the <body> element on the front end. Interestingly, it also applies the padding to the .editor-styles-wrapper class so the spacing is displayed when working in the back-end Block Editor. Pretty cool!

I was able to confirm those CSS custom properties in DevTools while digging around.

Enabling useRootPaddingAwareAlignments also applies left and right padding to any block that supports the “content” width and “wide” width values in the Global Styles image above. We can also define those values in theme.json:

{
  "version": 2,
  "settings": {
    "layout": {
      "contentSize": "640px",
      "wideSize": "1000px"
    }
  }
}

If the Global Styles settings are different than what is defined in theme.json, then the Global Styles take precedence. You can learn all about managing block theme styles in my last article.

  • contentSize is the default width for blocks.
  • wideSize provides a “wide” layout option and establishes a wider column for blocks to stretch out.

So, that last code example will give us the following CSS:

/* The default content container */
.wp-container-[id] > * {
  max-width: 640px;
  margin-left: auto !important;
  margin-right: auto !important;
}

/* The wider content container */
.wp-container-[id] > .alignwide {
  max-width: 1000px;
}

[id] indicates a unique number automatically generated by WordPress.

But guess what else we get? Full alignment as well!

.wp-container-[id] .alignfull {
  max-width: none;
}

See that? By enabling useRootPaddingAwareAlignments and defining contentSize and wideSize, we also get a full alignment CSS class for a total of three container configurations for controlling the width of blocks that are added to pages and posts.

This applies to the following layout-specific blocks: Columns, Group, Post Content, and Query Loop.

Block layout controls

Let’s say we add any of those aforementioned layout-specific blocks to a page. When we select the block, the block settings UI offers us new layout settings based on the settings.layout values we defined in theme.json (or the Global Styles UI).

We’re dealing with very specific blocks here — ones that can have other blocks nested inside. So, these Layout settings are really about controlling the width and alignment of those nested blocks. The “Inner blocks use content width” setting is enabled by default. If we toggle it off, then we have no max-width on the container and the blocks inside it go edge-to-edge.

If we leave the toggle on, then nested blocks will adhere to either the contentWidth or wideWidth values (more on that in a bit). Or we can use the numeric inputs to define custom contentWidth and wideWidth values in this one-off instance. That’s great flexibility!

Wide blocks

The settings we just looked are set on the parent block. Once we’ve nested a block inside and select it, we have additional options in that block to use the contentWidth, wideWidth, or go full-width.

This Image block is set to respect the contentWidth setting, labeled “None” in the contextual menu, but can also be set to wideWidth (labeled “Wide width”) or “Full width”.

Notice how WordPress multiplies the root-level padding CSS custom properties by -1 to create negative margins when selecting the “Full width” option.

The .alignfull class sets negative margins on a nested block to ensure it takes up the full viewport width without conflicting with the root-level padding settings.

Using a constrained layout

We just covered the new spacing and alignments we get with WordPress 6.1. Those are specific to blocks and any nested blocks within blocks. But WordPress 6.1 also introduces new layout features for even more flexibility and consistency in a theme’s templates.

Case in point: WordPress has completely restructured its Flex and Flow layout types and gave us a constrained layout type that makes it easier to align block layouts in themes using the content width settings in the Site Editor’s Global Styles UI.

Flex, Flow, and Constrained layouts

The difference between these three layout types is the styles that they output. Isabel Brison has an excellent write-up that nicely outlines the differences, but let’s paraphrase them here for reference:

  • 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.

This new slate of layout types creates semantic class names for each layout:

Semantic layout classLayout typeSupported blocks
.is-layout-flowFlow layoutColumns, Group, Post Content, and Query Loop.
.is-layout-constrainedConstrained layoutColumns, Group, Post Content, and Query Loop.
.is-layout-flexFlex layoutColumns, Buttons, Social Icons

Justin Tadlock has an extensive write-up on the different layout types and semantic classes, including use cases and examples.

Updating your theme to support constrained layouts

If you’re already using a block theme of your own making, you’re going to want to update it to support constrained layouts. All it takes is swapping out a couple of things in theme.json:

{
  "version": 2,
  "settings": {
    "layout": {
      "type": "constrained", // replaces `"inherit": true`
      "type": "default", // replaces `"inherit": false`
    }
  }
}

These are recently released block themes that have enabled spacing settings with useRootPaddingAwareAlignments and have an updated theme.json file that defines a constrained layout:

ThemeRoot-level paddingConstrained layout features
TT3Source codeSource codeTemplates
ProWPSource codeSource codeTemplates
TriangulateSource codeSource codeTemplates
OaknutSource codeSource codeTemplates
LoudnessSource codeSource codeTemplates
PixlSource codeSource codeTemplates
Block CanvasSource codeSource code, Templates
RainfallSource codeSource codeTemplates

Disabling layout styles

The base layout styles are default features that ship in WordPress 6.1 Core. In other words, they’re enabled right out of the box. But we can disable them if we need to with this little snippet in functions.php:

// Remove layout styles.
add_theme_support( 'disable-layout-styles' );

Big warning here: disabling support for the default layout types also removes all of the base styling for those layouts. That means you’ll need to roll your own styles for spacing, alignments, and anything else needed to display content in different template and block contexts.

Wrapping up

As a great fan of full-width images, the new contained WordPress 6.1 layout and padding aware alignment features are two of my most favorites yet. Taken together with other tools including, better margin and padding control, fluid typography, and updated List and Quote blocks, among others, is solid proof that WordPress is moving towards a better content creation experience.

Now, we have to wait and look at how the imagination and creativity of ordinary designers and content creators use these incredible tools and take it to a new level.

Because of the site editor development iterations in progress, we should always anticipate a difficult path ahead. However, as an optimist, I am eager to see what will happen in the upcoming version of WordPress 6.2. Some of the thing, that I am keeping a close eye on are things like features being considered for inclusion, support for sticky positioning, new layout class names for inner block wrappers, updated footer alignment options, and adding constrained and flow layout options to Cover blocks.

This GitHub issues #44720 lists the layout related discussions slated for WordPress 6.2.

Additional resources

I consulted and referenced a lot of sources while digging into all of this. Here’s a big ol’ list of things I found helpful and think you might enjoy as well.

Tutorials

WordPress posts

GitHub pull requests and issues


Using The New Constrained Layout 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/using-the-new-constrained-layout-in-wordpress-block-themes/feed/ 5 375278
WordPress Developer Blog https://css-tricks.com/wordpress-developer-blog/ https://css-tricks.com/wordpress-developer-blog/#comments Tue, 22 Nov 2022 18:36:12 +0000 https://css-tricks.com/?p=375599 Well, hey check this out. Looks like there is a brand spankin’ new blog over at WordPress.org all about WordPress development. In the original proposal for the blog, Birgit Pauli-Haak writes:

The Make Core blog has a heavy


WordPress Developer Blog originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Well, hey check this out. Looks like there is a brand spankin’ new blog over at WordPress.org all about WordPress development. In the original proposal for the blog, Birgit Pauli-Haak writes:

The Make Core blog has a heavy emphasis on meeting notes for the various core teams, rather than highlighting new features. This makes it difficult for developers who are not contributors or who just occasionally contribute to find the relevant information among the team-related posts.

Josepha describes the blog further in the announcement post:

These are types of content that lend themselves more toward the long-form content of a blog.  However, there are more practical reasons for this new home for developers on WordPress.org:

  • Posts that detail updated or new APIs.
  • A way to subscribe to development-related updates.
  • A place to keep up with ongoing discussions.

Perhaps the most important reason for the Developer Blog is to have a central place for WordPress extenders.  Information can fragment across various sites, and developers spend valuable time seeking it out.  This blog is an attempt to provide a curated experience of the most important updates. 

Hear, hear! This is exactly the sort of thing I feel has been missing in the WordPress development space: quality information from established developers that shares useful tips, tricks, and best practices for working with WordPress in this new era of full-site editing. With WordPress Core development taking place at break-neck speeds, having a central source of updated information and a way to syndicate it is a welcome enhancement for sure.

There are already a few excellent articles in there to kick-start things:

It’s WordPress, of course, so anyone and everyone is encouraged to contribute. If you do, it’s a good idea to first check our the writing tips and guidelines. And, naturally, there is an RSS feed you can use to keep up with the lastest posts.

If you wanna go down the ol’ rabbit trail for how the blog came together, here are a few links to get that context:

(High fives to Ganesh Dahal for the tip!)

To Shared LinkPermalink on CSS-Tricks


WordPress Developer Blog originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/wordpress-developer-blog/feed/ 2 375599
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
Managing CSS Styles in a WordPress Block Theme https://css-tricks.com/managing-css-styles-in-a-wordpress-block-theme/ https://css-tricks.com/managing-css-styles-in-a-wordpress-block-theme/#comments Mon, 07 Nov 2022 14:05:11 +0000 https://css-tricks.com/?p=374910 The way we write CSS for WordPress themes is in the midst of sweeping changes. I recently shared a technique for adding fluid type support in WordPress by way of theme.json, a new file that WordPress has been pushing


Managing CSS Styles in a WordPress Block Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The way we write CSS for WordPress themes is in the midst of sweeping changes. I recently shared a technique for adding fluid type support in WordPress by way of theme.json, a new file that WordPress has been pushing hard to become a central source of truth for defining styles in WordPress themes that support full-site editing (FSE) features.

Wait, no style.css file? We still have that. In fact, style.css is still a required file in block themes, though its role is greatly reduced to meta information used for registering the theme. That said, the fact is that theme.json is still in active development, meaning we’re in a transitional period where you might find styles defined there, in styles.css or even at the block level.

So, what does styling actually look like in these WordPress FSE days? That’s what I want to cover in this article. There’s a lack of documentation for styling block themes in the WordPress Theme Developer Handbook, so everything we’re covering here is what I’ve gathered about where things currently are as well as discussions about the future of WordPress theming.

The evolution of WordPress styles

The new developmental features that are included in WordPress 6.1 get us closer to a system of styles that are completely defined in theme.json, but there is still be plenty of work to do before we can fully lean on it. One way we can get an idea of what’s coming in future releases is by using the Gutenberg plugin. This is where experimental features are often given a dry run.

Another way we can get a feel for where we are is by looking at the evolution of default WordPress themes. To date, there are three default themes that support full-site editing:

But don’t start trading the CSS in style.css for JSON property-value pairs in theme.json just yet. There are still CSS styling rules that need to be supported in theme.json before we think about doing that. The remaining significant issues are currently being discussed with an aim to fully move all the CSS style rules into theme.json and consolidate different sources of theme.json into a UI for for setting global styles directly in the WordPress Site Editor.

The Global Styles UI is integrated with the right panel in the Site Editor. (Credit: Learn WordPress)

That leaves us in a relatively tough spot. Not only is there no clear path for overriding theme styles, but it’s unclear where the source of those styles even come from — is it from different layers of theme.json files, style.css, the Gutenberg plugin, or somewhere else?

Why theme.json instead of style.css?

You might be wondering why WordPress is moving toward a JSON-based definition of styles instead of a traditional CSS file. Ben Dwyer from the Gutenberg development team eloquently articulates why the theme.json approach is a benefit for theme developers.

It’s worth reading Ben’s post, but the meat is in this quote:

Overriding CSS, whether layout, preset, or block styles, presents an obstacle to integration and interoperability: visual parity between the frontend and editor becomes more difficult to maintain, upgrades to block internals may conflict with overrides. Custom CSS is, furthermore, less portable across other block themes.

By encouraging theme authors to use theme.json API where possible, the hierarchy of “base > theme > user” defined styles can be resolved correctly.

One of the major benefits of moving CSS to JSON is that JSON is a machine-readable format, which means it can be exposed in the WordPress Site Editor UI by fetching an API, thus allowing users to modify default values and customize a site’s appearance without writing any CSS at all. It also provides a way to style blocks consistently, while providing a structure that creates layers of specificity such that the user settings take the highest priority over those defined in theme.json. That interplay between theme-level styles in theme.json and the user-defined styles in the Global Styles UI is what makes the JSON approach so appealing.

Developers maintain consistency in JSON, and users gain flexibility with code-less customizations. That’s a win-win.

There are other benefits, for sure, and Mike McAlister from WP Engine lists several in this Twitter thread. You can find even more benefits in this in-depth discussion over at the Make WordPress Core blog. And once you’ve given that a read, compare the benefits of the JSON approach with the available ways to define and override styles in classic themes.

Defining styles with JSON elements

We’ve already seen a lot of progress as far as what parts of a theme theme.json is capable of styling. Prior to WordPress 6.1, all we could really do was style headings and links. Now, with WordPress 6.1, we can add buttons, captions, citations, and headings to the mix.

And we do that by defining JSON elements. Think of elements as individual components that live in a WordPress block. Say we have a block that contains a heading, a paragraph, and a button. Those individual pieces are elements, and there’s an elements object in theme.json where we define their styles:

{
  "version": 2,
  "settings": {},
  // etc.
  "styles": {
    // etc.
    "elements": {
        "button": { ... },
        "h1": { ... },
        "heading": { ... },
    },
  },
  "templateParts": {}
}

A better way to describe JSON elements is as low-level components for themes and blocks that do not need the complexity of blocks. They are representations of HTML primitives that are not defined in a block but can be used across blocks. How they are supported in WordPress (and the Gutenberg plugin) is described in the Block Editor Handbook and this Full Site Editing tutorial by Carolina Nymark.

For example, links are styled in the elements object but are not a block in their own right. But a link can be used in a block and it will inherit the styles defined on the elements.link object in theme.json. This doesn’t fully encapsulate the definition of an element, though, as some elements are also registered as blocks, such as the Heading and Button blocks — but those blocks can still be used within other blocks.

Here is a table of the elements that are currently available to style in theme.json prior to WordPress 6.1, courtesy of Carolina:

ElementSelectorWhere it’s supported
link<a>WordPress Core
h1 – h6The HTML tag for each heading level: <h1><h2><h3><h4><h5> and <h6>WordPress Core
headingStyles all headings globally by individual HTML tag: <h1><h2><h3><h4><h5> and <h6>Gutenberg plugin
button.wp-element-button.wp-block-button__linkGutenberg plugin
caption.wp-element-caption,
.wp-block-audio figcaption,
.wp-block-embed figcaption,
.wp-block-gallery figcaption,
.wp-block-image figcaption,
.wp-block-table figcaption,
.wp-block-video figcaption
Gutenberg plugin
cite.wp-block-pullquote citeGutenberg plugin

As you can see, it’s still early days and plenty still needs to move from the Gutenberg plugin into WordPress Core. But you can see how quick it would be to do something like style all headings in a theme globally without hunting for selectors in CSS files or DevTools.

Further, you can also start to see how the structure of theme.json sort of forms layers of specificity, going from global elements (e.g. headings) to individual elements (e.g. h1), and block-level styles (e.g. h1 contained in a block).

Additional information on JSON elements is available in this Make WordPress proposal and this open ticket in the Gutenberg plugin’s GitHub repo.

JSON and CSS specificity

Let’s keep talking about CSS specificity. I mentioned earlier that the JSON approach to styling establishes a hierarchy. And it’s true. Styles that are defined on JSON elements in theme.json are considered default theme styles. And anything that is set by the user in the Global Styles UI will override the defaults.

In other words: user styles carry more specificity than default theme styles. Let’s take a look at the Button block to get a feel for how this works.

I’m using Emptytheme, a blank WordPress theme with no CSS styling. I’m going to add a Button block on a new page.

The background color, text color, and rounded borders come from the block editor’s default settings.

OK, we know that WordPress Core ships with some light styling. Now, I’m going to switch to the default TT3 theme from WordPress 6.1 and activate it. If I refresh my page with the button, the button changes styles.

The background color, text color, and rounded corner styles have changed.

You can see exactly where those new styles are coming from in TT3’s theme.json file. This tells us that the JSON element styles take precedence over WordPress Core styles.

Now I am going to modify TT3 by overriding it with a theme.json file in a child theme, where the default background color of the Button block is set to red.

The default style for the Button block has been updated to red.

But notice the search button in that last screenshot. It should be red, too, right? That must mean it is styled at another level if the change I made is at the global level. If we want to change both buttons, we could do it at the user level using the Global Styles UI in the site editor.

We changed the background color of both buttons to blue and modified the text as well using the Global styles UI. Notice that the blue from there took precedence over the theme styles!

The Style Engine

That’s a very quick, but good, idea of how CSS specificity is managed in WordPress block themes. But it’s not the complete picture because it’s still unclear where those styles are generated. WordPress has its own default styles that come from somewhere, consumes the data in theme.json for more style rules, and overrides those with anything set in Global Styles.

Are those styles inline? Are they in a separate stylesheet? Maybe they’re injected on the page in a <script>?

That’s what the new Style Engine is hopefully going to solve. 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. I know, I know. Yet another abstraction on top of other abstractions just to author some styles. But having a centralized API for styles is probably the most elegant solution given that styles can come from a number of places.

We’re only getting a first look at the Style Engine. In fact, here’s what has been completed so far, according to the ticket:

  • Audit and consolidate where the code generates block support CSS in the back end so that they are delivered from the same place (as opposed to multiple places). This covers CSS rules such as margin, padding, typography, colors, and borders.
  • Remove repetitive layout-specific styles and generate semantic class names.
  • Reduce the number of inline style tags we print to the page for block, layout, and element support.

Basically, this is the foundation for establishing a single API that contains all the CSS style rules for a theme, wherever they come from. It cleans up the way WordPress would inject inline styles pre-6.1 and establishes a system for semantic class names.

Further details on the long-term and short-term goals of Style Engine can be found in this Make WordPress Core discussion. You can also follow the tracking issue and project board for more updates.

Working with JSON elements

We talked a bit about JSON elements in the theme.json file and how they are basically HTML primitives for defining default styles for things like headings, buttons, and links, among others. Now, let’s look at actually using a JSON element and how it behaves in various styling contexts.

JSON elements generally have two contexts: the global level and the block level. The global level styles are defined with less specificity than they are at the block level to ensure that block-specific styles take precedence for consistency wherever blocks are used.

Global styles for JSON elements

Let’s look at the new default TT3 theme and examine how its buttons are styled. The following is an abbreviated copy-paste of the TT3 theme.json file (here’s the full code) showing the global styles section, but you can find the original code here.

View code
{
  "version": 2,
  "settings": {},
    // ...
  "styles": {
    // ...
    "elements": {
      "button": {
        "border": {
          "radius": "0"
        },
        "color": {
          "background": "var(--wp--preset--color--primary)",
          "text": "var(--wp--preset--color--contrast)"
        },
        ":hover": {
          "color": {
            "background": "var(--wp--preset--color--contrast)",
            "text": "var(--wp--preset--color--base)"
          }
        },
        ":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)"
          }
        }
      },
      "h1": {
        "typography": { }
      },
      // ...
      "heading": {
        "typography": {
          "fontWeight": "400",
          "lineHeight": "1.4"
      }
      },
      "link": {
        "color": {
          "text": "var(--wp--preset--color--contrast)"
        },
        ":hover": {
          "typography": {
            "textDecoration": "none"
          }
        },
        ":focus": {
          "typography": {
            "textDecoration": "underline dashed"
          }
        },
        ":active": {
          "color": {
            "text": "var(--wp--preset--color--secondary)"
          },
          "typography": {
            "textDecoration": "none"
          }
        },
        "typography": {
          "textDecoration": "underline"
        }
      }
     },
    // ...
  },
  "templateParts": {}
}

All buttons are styled at the global level (styles.elements.button).

Every button in the Twenty Twenty-Three theme shares the same background color, which is set to the --wp--preset--color--primary CSS variable, or #B4FD55.

We can confirm this in DevTools as well. Notice that a class called .wp-element-button is the selector. The same class is used to style the interactive states as well.

Again, this styling is all happening at the global level, coming from theme.json. Whenever we use a button, it is going to have the same background because they share the same selector and no other style rules are overriding it.

As an aside, WordPress 6.1 added support for styling interactive states for certain elements, like buttons and links, using pseudo-classes in theme.json — including :hover, :focus, and :active — or the Global Styles UI. Automattic Engineer Dave Smith demonstrates this feature in a YouTube video.

We could override the button’s background color either in theme.json (preferably in a child theme since we’re using a default WordPress theme) or in the Global Styles settings in the site editor (no child theme needed since it does not require a code change).

But then the buttons will change all at once. What if we want to override the background color when the button is part of a certain block? That’s where block-level styles come into play.

Block-level styles for elements

To understand how we can use and customize styles at the block level, let’s change the background color of the button that is contained in the Search block. Remember, there is a Button block, but what we’re doing is overriding the background color at the block level of the Search block. That way, we’re only applying the new color there as opposed to applying it globally to all buttons.

To do that, we define the styles on the styles.blocks object in theme.json. That’s right, if we define the global styles for all buttons on styles.elements, we can define the block-specific styles for button elements on styles.block, which follows a similar structure:

{
  "version": 2,
  // ...
  "styles": {
    // Global-level syles
    "elements": { },
    // Block-level styles
    "blocks": {
      "core/search": {
        "elements": {
          "button": {
            "color": {
              "background": "var(--wp--preset--color--quaternary)",
              "text": "var(--wp--preset--color--base)"
            }
          }
        },
        // ...
      }
    }
  }
}

See that? I set the background and text properties on styles.blocks.core/search.elements.button with two CSS variables that are preset in WordPress.

The result? The search button is now red (--wp--preset--color--quaternary), and the default Button block retains its bright green background.

We can see the change in DevTools as well.

The same is true if we want to style buttons that are included in other blocks. And buttons are merely one example, so let’s look at another one.

Example: Styling headings at each level

Let’s drive all this information home with an example. This time, we will:

  • Style all headings globally
  • Style all Heading 2 elements
  • Style Heading 2 elements in the Query Loop block

First, let’s start with the basic structure for theme.json:

{
  "version": 2,
  "styles": {
    // Global-level syles
    "elements": { },
    // Block-level styles
    "blocks": { }
  }
}

This establishes the outline for our global and block-level styles.

Style all headings globally

Let’s add the headings object to our global styles and apply some styles:

{
  "version": 2,
  "styles": {
    // Global-level syles
    "elements": {
      "heading": {
        "color": "var(--wp--preset--color--base)"
      },
    },
    // Block-level styles
    "blocks": { }
  }
}

That sets the color for all headings to the preset base color in WordPress. Let’s change the color and font size of Heading 2 elements at the global level as well:

{
  "version": 2,
  "styles": {
    // Global-level syles
    "elements": {
      "heading": {
        "color": "var(--wp--preset--color--base)"
      },
      "h2": {
        "color": "var(--wp--preset--color--primary)",
        "typography": {
          "fontSize": "clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem)"
        }
      }
    },
    // Block-level styles
    "blocks": { }
  }
}

Now, all Heading 2 elements are set to be the primary preset color with a fluid font size. But maybe we want a fixed fontSize for the Heading 2 element when it is used in the Query Look block:

{
  "version": 2,
  "styles": {
    // Global-level syles
    "elements": {
      "heading": {
        "color": "var(--wp--preset--color--base)"
      },
      "h2": {
        "color": "var(--wp--preset--color--primary)",
        "typography": {
          "fontSize": "clamp(2.625rem, calc(2.625rem + ((1vw - 0.48rem) * 8.4135)), 3.25rem)"
        }
      }
    },
    // Block-level styles
    "blocks": {
      "core/query": {
        "elements": {
          "h2": {
            "typography": {
              "fontSize": 3.25rem
            }
          }
        }
      }
    }
  }
}

Now we have three levels of styles for Heading 2 elements: all headings, all Heading 2 elements, and Heading 2 elements that are used in the Query Loop block.

Existing theme examples

While we only looked at the styling examples for buttons and headings in this article, WordPress 6.1 supports styling additional elements. There’s a table outlining them in the “Defining styles with JSON elements” section.

You’re probably wondering which JSON elements support which CSS properties, not to mention how you would even declare those. While we wait for official documentation, the best resources we have are going to be the theme.json files for existing themes. I’m going to provide links to themes based on the elements they customize, and point out what properties are customized.

ThemeWhat’s customizedTheme JSON
BlockbaseButtons, headings, links, core blocksSource code
Block CanvasButtons, headings, links, core blocksSource code
DiscoButtons, headings, core blocksSource code
FrostButtons, headings, links, captions, cite, core blocksSource code
PixlButtons, headings, links, core blocksSource code
RainfallButtons, headings, links, core blocksSource code
Twenty Twenty-ThreeButtons, headings, links, core blocksSource code
VivreButtons, headings, links, core blocksSource code

Be sure to give each theme.json file a good look because these themes include excellent examples of block-level styling on the styles.blocks object.

Wrapping up

Frequent changes to the full-site editor are becoming a major sources of irritation to many people, including tech-savvy Gutenberg users. Even very simple CSS rules, which work well with classic themes, don’t seem to work for block themes because of the new layers of specificity we covered earlier.

Regarding a GitHub proposal to re-design the site editor in a new browser mode, Sara Gooding writes in a WP Tavern post:

It’s easy to get lost while trying to get around the Site Editor unless you are working day and night inside the tool. The navigation is jumpy and confusing, especially when going from template browsing to template editing to modifying individual blocks.

Even as a keen early rider in the world of Gutenberg block editor and block-eye themes, I do have tons of my own frustrations. I’m optimistic, though, and anticipate that the site editor, once completed, will be a revolutionary tool for users and techno-savvy theme developers alike. This hopeful tweet already confirms that. In the meantime, it seems that we should be preparing for more changes, and perhaps even a bumpy ride.

References

I’m listing all of the resources I used while researching information for this article.

JSON elements

Global Styles

Style Engine


Thanks for reading! I’d love to hear your own reflections on using the block themes and how you managing your CSS.


Managing CSS Styles in a WordPress Block Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/managing-css-styles-in-a-wordpress-block-theme/feed/ 7 374910
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