Styling Headless WordPress with SCSS modules.

- by Colin on 1-3-2021

There are many styling configurations, from plain CSS to libraries like Bootstrap to JSS like styled-components or Material-UI. In this post, I’m going to explain how I configured Headless WordPress with SCSS and modules.

Styling Decisions

Lots of styles
Lot of stylesheets

Combining Gatsby and WordPress offers interesting styling decisions. Traditionally WordPress themes manage and enqueue stylesheets into the HTML document. Furthermore, the Gutenberg Editor requires specific stylesheets for blocks to work correctly, and plugins often enqueue third party styles. I have seen WordPress sites that have had over twenty stylesheets between the <head> tags.

When using Gatsby or any Static Site Generator, we must include all these stylesheets somewhere in the site configuration, assuming we want to use them. The default Block Editor provides an example of this. It would be excellent if the content creator can use the full features of the Gutenberg Editor.

Gutenberg Blocks

It’s essential to know and remember that the core Gutenberg blocks only output HTML. Though at the input stage, when building pages within the WordPress Gutenberg Editor, they are “live” React Components. It’s not easy to rehydrate content within the returned markup from the Gutenberg Editor.

To support the Gutenberg markup effectively, we need to install the @wordpress-block-library as a dependency and include the CSS somewhere within the Gatsby configuration. For example, the official Gatsby starter calls them in each template component, and this helps reinforce where the page query data becomes hydrated into the React tree. See the gatsby-node.js file to find the functions that invoke each page template.

https://github.com/gatsbyjs/gatsby-starter-wordpress-blog

 
// We're using Gutenberg so we need the block styles
import "@wordpress/block-library/build-style/style.css"
import "@wordpress/block-library/build-style/theme.css"
 

Configuring Sass

Firstly, to configure Gatsby to process SCSS to CSS, you will need to install the following.

gatsby-plugin-sass sass

Using Sass makes it easy to manage and import third party CSS and offers many beneficial features such as variables, functions, mixins and placeholders that simplify styling strategies. The WordPress Block source is compiled from SCSS, and many popular libraries, such as Bootstrap, are utilising SCSS.

https://sass-lang.com/

Component Architecture

However, Gatsby is React and utilises a component-like architecture. Typically each component is responsible for its styling and often relies on props to configure. It differs somewhat from the page-template hierarchy used in WordPress, where a global stylesheet is traditionally responsible for much of the site markup.

SCSS Directory

I decided to try and use both methods. Use a global stylesheet to manage Gutenberg and default WordPress styles and also use SCSS modules to allow for component-level styling. The file architecture I am using looks a little like the list below.

  • ./src/sass
    • main.scss (global)
    • config-module.scss
    • Settings
      • _variables.scss
      • _colors.scss
      • _settings.scss
    • Tools
      • _functions.scss
      • _mixins.scss
      • _placeholders.scss
      • _tools.scss
    • Generic
      • _elements.scss
      • _gutenberg_default.scss
      • _wp_classes.scss
      • _generic.scss

All the styles included in the global main.scss can be used wherever necessary across components. It is convenient for managing page layouts such as wrappers, grids, sidebars, utility classes, third-party CSS, and Gutenberg classes.

  // main.scss 
/* SASS goodies, functions, mixins, variables and placeholders */
@import "0-settings/settings";
@import "1-tools/tools";
/* sitewide styling */
@import "2-generic/generic";

Modules + SCSS

Create-React-App and Gatsby both support modules by default. The main advantages are that each module uses a namespace, meaning there are no conflicts between component CSS and that they make it easy to import styles directly into JavaScript.

To enable modules, they need to conform to the following naming convention, communicating to Webpack that we want to transpile the file as a module.

my-component.module.sccs

To utilise the SCSS features within component modules, import the config-module.scss at the beginning of each file as and where needed.

 // config-module.scss
/* SASS goodies, functions, mixins, variables and placeholders */
@import "0-settings/settings";
@import "1-tools/tools";

/* Like to keep composable classes here. */
.composable-class { text-align: left;}

/* property exists on all **.module.scss that import this file*/
:export {
// sass $variable to JS
mainTextColor: $mainTextColor;
}

We can now refer to all of the SCSS goodies within each module. This is particularly useful for managing media queries via mixins by using the sass-mq library.

https://github.com/sass-mq/sass-mq

npm i sass-mq || yarn add sass-mq
 
// some-component.module.scss
/* SASS goodies, functions, mixins, variables and placeholders */
@import "path/to/src/sass/config-module.scss";

> div {
    // sass-mq mixin
    @include mq($from: 500px) {
      flex-direction: row;
      width: calc(50% - 35px);
      margin-right: 0.25rem;
      margin-left: 0.25rem;
    }

    @include mq($from: 768px) {
      width: 30%;
      flex-direction: column;
    }
 }

JavaScript Imports

Utilising modules also simplifies exporting styles directly into Javascript by using the export property.

  // some-component.module.scss
/* SASS goodies, functions, mixins, variables and placeholders */
@import "path/to/src/sass/config-module.scss";

.some-component-class {
  background: red;
}
:export {
// sass $variable to JS
currentTextColor: $currentTextColor;
}

Styles can be exported from the module file and directly imported into the JSX. Notice how the class syntax changes from dashed-spacing to camel casing.

 
// SomeComponent.js
import styles from "./some-component.module.scss";

const textColor = styles.mainTextColor;
const componentClass = styles.someComponentClass;
const objectPropStringSyntax = = styles['someComponentClass'];
export const SomeComponent => ({props}) =>...

These features are incredibly versatile, and it’s easy to render a class or inline style within JSX conditionally. It is simplified further using this library.

https://github.com/JedWatson/classnames

Composable classes

Using modules also enables us to compose classes together. However, this only works in the parent selector. Using a mixin gets around this.

 /* another-component.module.scss */
@import "path/to/src/sass/config-module.scss";

.another-component {
  // see config-module
  composes: composable-class;

  > div {
    // composes: composable-class; - won't work in nested - use mixin
    @include someMixin;
  }
}

Gotchas and edge-cases

Crucially modules will always overrule global styles, but they won’t necessarily overrule each other. Their position in the React component tree makes a difference, but sometimes a selector using an ancestor utilising a > to target immediately nested components could overrule descendant modules.

It’s all the more important as the incoming data expected from WordPress is likely to contain large HTML structures that rely on global styles. It isn’t necessarily a setback, and we can use this to our advantage.

Namespaces

As modules use an individual namespace, if a single module.scss is referred to from multiple components, it will spit out different namespaces. It’s also crucial if JavaScript uses the string as a condition of the imported class in JSX. Perhaps there are situations where it’s more effective passing classes to child components as props.

The Transpiled CSS

It is possible to use third-party CSS inside a module, and this will output with a namespace. Useful for features like menus but could potentially bloat stylesheets with duplicate CSS if used to add utility components. The same is the case for sass mixins, placeholders and functions. 

In Summary

Sass is super versatile and provides a solution for bundling Gutenberg and the default WordPress styles. Or any other third party CSS libraries into a Gatsby site. Using modules allows us to import classes and properties directly into JavaScript while also providing component-level organisation backed by name-spaced selectors.

Problems and examples.

I’ve noticed that the Gutenberg button styling doesn’t seem to work correctly with the demo content provided by the WordPress unit test XML.

Block: Button

Galleries also aren’t fully supported from the default WordPress block styles.

Block: Gallery

Some of the other block features work very effectively.

Block: Columns

Block: Image

Block category: Common

Block: Cover