Using context variables in SCSS

In newer WordPress projects I build for clients, I use plain CSS files, creating individual files for each block type. These are then automatically loaded in the Block Editor and in the frontend using this PHP function. Each file is only loaded in the frontend if the block is actually used on the page or post being viewed.

Using separate plain CSS files allows me to omit the build process I’ve been using for over a decade and I’ll be honest, I’m glad to see the back of overly-complex structures using mixins and functions. In the last few projects I’ve built, I’ve found that everything I need to do can be achieved without compiling, thanks to CSS Custom Properties and other CSS functions, all of which run directly in the browser.

However, I still maintain older projects and so, occasionally, I need to revisit and restructure the CSS so that it will continue to work in current browsers and so that the gulp build process continues to work without complaint. (In particular, because of the deprecation of @import in SCSS compilation last year.)

This is largely pain-free: instead of pulling in mixins, functions and variables from a set of central files in the global scope, I need to load the file containing the mixin (for example) and then use a namespace to reference the mixin.

Using mixins before

// The 'typo' mixin used to be in the global scope

selector {
    @include typo(16,24,10);
}

Using mixins now

@use '../mixins/typography' as typography_mixins;

selector {
    @include typography_mixins.typo(16,24,10);
}

This is a bit longer-winded than before, but ensures that naming conflicts are avoided. Pulling in the mixins on-demand also means that I can remove all of the superfluous @use rules from the main entry point files. Alongside the compatibility resolutions, this also helps me to debug CSS problems because everything for a component is clearly contained within one file.

Sharing SCSS in the frontend and in the editor

Although the individual CSS files for the block types remain largely untouched by this change, my SCSS build process uses main two entry points for definitions which don’t fall within the block construct. For example, component selectors for reusable styles across multiple blocks. (Examples are .c-blocks or .c-card, which are reused throughout the site in custom HTML and in my own custom blocks.)

The two entry points are view.scss and editor.scss. Each of them generates its own CSS file (through my gulp process). view.css is enqueued in the frontend and editor.css in the Block Editor. Sharing SCSS in the build process allows me to load both files with common rules for both destinations.

However, there are some rules which are only intended for one or other of these contexts (view or editor). I like to keep all of the rules for a single component in one file, so I need to pull the component file in using a context definition in each case. In the following simplified example, the border colour of a card should be different in the editor than on the website.

(My build process uses a customised version of gulp-editor-styles to automatically convert body to .editor-styles-wrapper in CSS which is destined for the Block Editor.)

// Ensure that the $config object is defined in case it's not passed in.
$config: (
    context: 'view',
) !default;

body {
    --c-card--border-color: currentColor;
}

.c-card {
    border: 1px solid var(--c-card--border-color);
}

@if map.get($config, context) == 'edit' {
    body {
        --c-card--border-color: red;
    }
}

If I don’t specify a value for $config in my main entry point file, then the default value of view will be used, and the rule for the editor context won’t be included in the resultant CSS file. The @use commands in the separate entry point files therefore differ slightly.

@use 'components/card' with (
    $config: (
        context: 'view',
    )
);
@use 'components/card' with (
    $config: (
        context: 'edit',
    )
);

Leave a Reply

Your email address will not be published. Required fields are marked *