Front end Developer
CKEditor5 scoped styles with PostCSS
The Drupal 10 update is moving to CKEditor 5. What’s different? It’s no longer an iframe! So how do we scope any custom styles we want to include?
First and foremost, if you haven’t read the Drupal docs about including custom styles, then please read that. It will give you the necessary context.
This article is specifically about a theme setup that already uses PostCSS (as opposed to Sass - this is much easier with Sass), has various component stylesheets, and includes some level of theme CSS to the CKEditor window to help improve the content editor experience.
Simply including the same styles used in CKEditor 4 to CKEditor 5 now bleed out into the admin theme, so we need to scope them to the .ck-content
class.
Let's just say the theme's original ckeditor.css
file looks something like this:
@import "./custom-properties.css";
@import "./base/base.css";
@import "./button/button.css";
@import "./layout/layout.css";
body {
font-family: var(--font-family);
padding: .5rem;
}
We use a couple of PostCSS plugins already, notably postcss-import
and postcss-preset-env
but in order to wrap everything here in a certain class, including everything we’re importing…
We need a new plugin
I tested a few class prefixing plugins, but the only one that suited my requirements was postcss-prefixwrap. With it installed, here’s our basic postcss.config.js
:
module.exports = {
plugins: [
require("postcss-import"),
... other plugins
require("postcss-prefixwrap")(".ck-content", {
whitelist: ["docroot/themes/my_theme/src/ckeditor.css"],
nested: "&",
ignoredSelectors: [":root", /^\\.ck-(.+)$/],
}),
],
}
We place it last; we add the class to use as the prefix .ck-content
and specify our options;
- whitelist: our
ckeditor.css
, so this plugin only applies to that file. - nested: because we use ampersand for nested selectors
- ignoredSelectors: include the
:root
where we have some global custom properties and any class that starts with.ck-
because it’s already scoped.
The only change we need to make to our ckeditor.css
file is to change our body
styles to live on .ck-content
instead. Otherwise, they’ll come out at .ck-content body
and be ignored.
Now every selector in our generated ckeditor.css
file (that we didn’t ignore) is prefixed with .ck-content
; thus all the styles are scoped to CKEditor 5 and no longer bleed into the admin theme.
A note on CSS Custom Properties
If you (like me) prefer to preserve your custom properties, you may find the need to scope them as well, to avoid clashes with custom properties the admin theme might have.
For this, I added one more PostCSS plugin postcss-variables-prefixer;
module.exports = {
plugins: [
require("postcss-import"),
... other plugins
require("postcss-variables-prefixer")({
prefix: "pnx-",
}),
require("postcss-prefixwrap")(".ck-content", {
whitelist: ["docroot/themes/my_theme/src/ckeditor.css"],
nested: "&",
ignoredSelectors: [":root", /^\\.ck-(.+)$/],
}),
],
}
This isn’t specific to the ckeditor.css
file, but that’s ok with me; they can be unique everywhere.
Hopefully, this helps make the transition to CKEditor 5 a little smoother for your content editors when updating to Drupal 10.
Related Articles
Lightning talk: Custom CKEditor Widgets in Drupal 8
We're starting up our Lightning talks again during our weekly developer meetings here at PreviousNext. This week was about wiring up a straight forward plugin.js and extending CKEditorPluginBase to create a custom CKEditor widget in Drupal 8.
Watch the video for a run through of how this is done in Drupal 8.
Upgrading your site to Drupal 10
Upgrading from Drupal 9 to Drupal 10 requires preparation. These pointers will help you navigate the major version update and handle contrib and custom modules.
Optimise Your Page Loads with Lazy Loading Javascript
How to optimise your progressively decoupled Drupal frontend with the new Intersection Observer API.