Sage and Editor Styles

Hi,

I’m just getting to grips with Sage and seem to be going round in circles on a robust way of wrapping my CSS in the .editor-styles-wrapper selector for scoping my CSS in the block editor. In other projects I have used the PostCSS plugin postcss-prefix-selector for this and run 2 build steps, one for app.css and one for editor.css. I then end up with a second CSS file (editor.css) which is an exact copy of app.css but every rule is wrapped in .editor-styles-wrapper. I’m struggling to achieve this in Bud. Is this even the right approach or is there a better way? There are a number of posts on this on this forum which all suggest different solutions…

Thanks,

Hi there!

Can you show us the specific code that you’ve tried? What wasn’t working?

It’s the right approach if that’s what you’re wanting to do. Is there a better way? Maybe. It really depends on what you think works best for you, which is part of the reason why Sage has left it up to you to decide how you’d like to style things in the editor.

Different folks have different solutions. What approach works best for you and your colleagues?

Thanks for coming back Ben, and sorry for my lack of detail. So this is my bud config. It works without the call to app.postcss.setPluginOptions(). But when I include the setPluginOptions call I get 11 errors (1 per CSS dependency) saying Error: Loading PostCSS "/" plugin failed: Cannot find module '/'. I’m confident the options themselves are valid as I’ve copied and pasted them from my laravel mix config in another project. Appreciate your help!

export default async (app) => {

  app.postcss.setPlugins({
    ['tailwindcss']: await app.module.resolve('tailwindcss'),
    ['nesting']: await app.module.resolve('tailwindcss/nesting/index.js'),
    ['postcss-prefix-selector']: await app.module.resolve('postcss-prefix-selector'),
    ['autoprefixer']: await app.module.resolve('autoprefixer'),
  })

  app.postcss.setPluginOptions('postcss-prefix-selector', {
    prefix: '.editor-styles-wrapper',
      transform(prefix, selector, prefixedSelector, filePath, rule) {
        if (selector.match(/^(html|body)/)) {
          return selector.replace(/^([^\s]*)/, `$1 ${prefix}`);
        }
        
        if (filePath.match(/node_modules/)) {
          return selector; // Do not prefix styles imported from node_modules
        }
        
        const annotation = rule.prev();
        if (annotation?.type === 'comment' && annotation.text.trim() === 'no-prefix') {
          return selector; // Do not prefix style rules that are preceded by: /* no-prefix */
        }

        return prefixedSelector;
      },
    })

  app
    /**
     * Application entrypoints
     */
    .entry({
      app: ["@scripts/app", "@styles/app"],
      editor: ["@scripts/editor", "@styles/editor"],
    })

    /**
     * Directory contents to be included in the compilation
     */
    .assets(["images"])

    /**
     * Matched files trigger a page reload when modified
     */
    .watch(["resources/views/**/*", "app/**/*"])

    /**
     * Proxy origin (`WP_HOME`)
     */
    .proxy("http://epam.local")

    /**
     * Development origin
     */
    .serve("http://0.0.0.0:3000")

    /**
     * URI of the `public` directory
     */
    .setPublicPath("/app/themes/sage/public/")

};

My two cents here (of course, everyone should use what fits best): I would register the frontend styles as editor styles and let the Gutenberg Editor post-process those styles and wrap the selectors with .editor-styles-wrapper, see this example in the Sage 10 Full Site Editing (FSE) sample theme:

2 Likes

Thanks @strarsis. I had no idea Gutenberg offered this functionality! This seems a much more elegant solution. Sadly it doesn’t seem to be working for me right now, my styles are getting queued and are being loaded but are not being inlined of prefixed. Documentation seems a little sparse on this feature on the WP side. Anyway, it seems that this isn’t a Sage issue so I’ll do some more digging.

Whilst we’re on topic though, my current configuration (which is the default Sage 10 configuration with bud), is producing assets with a random string in the filename (ie. app.e3d516.css). How would I enqueue these without hard-coding this string, which changes each build? I suspect it has something to do with bundle() but can’t find anything on this in the docs.

Thanks.

This what asset('...') is used for in the example linked above. It can be used to get the correct path.

1 Like

For those that stumble across this, the missing piece was:

add_theme_support( 'editor-styles' );

This line is required in Wordpress to enable the automatic wrapping of CSS selectors in .editor-styles-wrapper.

So, for completeness, if you’d like to add your CSS to the block editor and have the selectors automatically prefixed by Wordpress you can remove the following from your sage starter theme:

 add_action('enqueue_block_editor_assets', function () {
     bundle('editor')->enqueue();
 }, 100);

and replace it with:

add_theme_support('editor-styles');
add_editor_style( asset( 'editor.css' )->relativePath( get_theme_file_path() ) );

Make sure your editor.css file has content. I just add @import 'app', as my editor styles aren’t complex and don’t need to be any different from app.css.

1 Like

Just tagging onto this for anyone using Tailwind. The classes will be wrapped in .editor-styles-wrapper which increases Tailwind’s specificity. This means that some core block styling is overwritten by Tailwind’s Preflight/reset.

To resolve this I disable preflight in tailwind.config.cjs:

module.exports = {
  corePlugins: {
    preflight: false,
  },
...

and then add my own reset. I found that tailwind borders don’t work if you disable Preflight unless you add this somewhere in your CSS.

*,
::before,
::after {
  border-width: 0;
  border-style: solid;
  border-color: theme('borderColor.DEFAULT', currentColor);
}
2 Likes

You may find this Sage 10 FSE theme helpful: