3

Although Drupal is designed to be styled using css files and configuration in the *.libraries.yml files, the CKEditor 5 module defines some styles in a Java Script file which:

  • Is included at the end of the page in Drupal so it overrides all styles defined in css files which are included in header.
  • Uses css variables to define various properties for styling purposes.

These variables are supposed to be modified as described in the CKEditor 5 Theme customization documentation. However, when a css file with custom values is included in Drupal, it's placed in the header which means the js overrides the custom values and not vice-versa.

So, the question is: as the CKEditor 5 is part of the Drupal core, what's the proper Drupal-way to override its css variables to customize the editor? Is there a Drupal documentation addressing this which I missed?

Example

The JS defines following style for the background of edited CKE field:

.ck.ck-editor__main>.ck-editor__editable {
    background: var(--ck-color-base-background);
    border-radius: 0;
}

So in order to change the background, the --ck-color-base-background variable should be modified.

What I Tried

Created the css/components/ckeditor.css file:

:root {
  --ck-color-base-background: green;
}

This attempts to modify the variable as described in the CKEditor 5 Theme customization documentation.

#app .ck-content {
  --ck-color-base-background: red;
}

This attempts to modify the variable a way I found on some page, regrettably lost the link and cannot find it again.

.ck.ck-editor__main > .ck-editor__editable {
  background: blue;
}

And this attempts to override the style directly.

Attempt #1

Include the custom css file in the my_theme.info.yml file as described in New API for adding theme-specific styles in CKEditor 5 and How to style custom content in CKEditor 5 Drupal documents:

ckeditor5-stylesheets:
  - css/components/ckeditor.css

Result:

  • The file is included in the middle of header.
  • The variable is unchanged and the custom style is overridden by default CKEditor style (from the CKE js.)

Attempt #2

Increase weight of the css file in the my_theme.info.yml file:

ckeditor5-stylesheets:
  - css/components/ckeditor.css: { weight: 100000 }

Result:

  • Failed with PHP error TypeError: strpos(): Argument #1 ($haystack) must be of type string, array given in strpos() (line 257 of core/lib/Drupal/Component/Utility/UrlHelper.php).

Attempt #3

Extend the core/ckeditor5 library.

In my_theme.info.yml file:

libraries-extend:
  core/ckeditor5:
    - my_theme/ckeditor5

In the my_theme.libraries.yml file:

ckeditor5:
  css:
    component:
      css/components/ckeditor.css: { weight: 100000 }

Result:

  • The custom css file is included last in the header.
  • The variable is unchanged and the custom style is overridden by default CKEditor style (from the CKE js.)

Attempt #4

The CKEditor 5 Theme customization documentation says that the css file can be imported in Java Script.

In the my_theme.libraries.yml file:

ckeditor5:
  css:
    component:
      css/components/ckeditor.css: { weight: 100000 }
  js:
    js/components/ckeditor.js: { attributes: { type: module } }

The custom ckeditor.js file:

/**
 * @module my_theme/ckeditor
 */

import '../../css/components/ckeditor.css';

Result:

  • The js file is included after the core/ckeditor5 libraries.
  • The js file fails with the Loading module from “.../css/components/ckeditor.css” was blocked because of a disallowed MIME type (“text/css”) error.

Attempt #5

In the Adding assets (CSS, JS) to a Drupal theme via *.libraries.yml Drupal documentation, there's mentioned header property. It's meant for Java Script, but let's try whether it works also for CSS.

In the my_theme.libraries.yml file:

ckeditor5:
  header: false
  css:
    component:
      css/components/ckeditor.css: { weight: 100000 }
  js:
    js/components/ckeditor.js: { attributes: { type: module } }

Result:

  • The custom css file is still in header.

Attempt #6

An answer to the How to make CSS and JS both load in the footer question mentions scope option.

In the my_theme.libraries.yml file:

ckeditor5:
  header: false
  css:
    component:
      css/components/ckeditor.css: { weight: 50, scope: footer }

Result:

  • The custom css file is still in header.

Technical Details

The style with variables I mentioned above is defined in the core/assets/vendor/ckeditor5/ckeditor5-dll/ckeditor5-dll.js file. It's already minified, so it's hard to read and figure out how it works, but it's the only place found by text search which sets it.

Other Links

  • CKEditor 5 background-color ignored in Olivero subtheme? Drupal issue - related to this, closed as "works as designed," but doesn't provide actual solution to changing CKE css variables, it just suggests:
    1. To use different, more specific css selector. This goes against the CKE documentation and doesn't answer to how generally modify these variables. Also, this is prone to errors as variables can be used by multiple styles, so one needs to be really careful to override them all.
    2. To override the relevant library. Are we really supposed to provide custom version of the ckeditor5-dll.js file?
    3. Move styles down with weight. As demonstrated above, this cannot work unless there's a way to move it between js files in the footer.
  • how can I style ckeditor5?( background-color and stuff ) question - basically same problem, but outside Drupal. The answer is to override the --ck-color-base-background which is what I'm trying to do, but can't figure how to do that in Drupal.
  • New API for adding theme-specific styles in CKEditor 5 Drupal page - at the end, there's a question asking similar thing, ignored.

Notes

  • I use updated Drupal 10 (via PHP Composer if that matters.)
  • During testing, I disabled merging css/js files.
  • After each attempt, I cleared cache and confirmed that the css/js file are actually included.
0

2 Answers 2

2

You could go with your Attempt #2 and use !important.

Add your custom stylesheet to yourtheme.info.yml

ckeditor5-stylesheets:
      - css/components/ckeditor5.css

Add your css. This colors the editor background red and the toolbar white.

    :root {
      --ck-color-base-background: red !important;
      --ck-color-toolbar-background: white !important;
    }
8
  • Does this mean that the CKEditor 5 integration is incomplete, this feature is not officially supported by Drupal, and must be hacked - for example the way you proposed?
    – Tom
    Commented May 5 at 13:13
  • It's not a hack though. It's the correct way to theme CKEditor in Drupal. You point to a stylesheet in your site theme like above, wherein you write your own styles for CKEditor. It was the same approach for CKEditor 4. You have to use important to override the :root variables, but that's completely valid CSS.
    – John
    Commented May 5 at 18:45
  • 1
    I think whether you'd class it as a hack or not is maybe beside the point - it's not an elegant solution, simply because it inhibits extensibility. Or at least makes it very awkward. The cascading nature, and specificity, that you would rely on in CSS, is out of the window as soon as you start using !important. Say Tom was creating a contrib module, wanted to override styles, and then make those styles themselves overridable by the end users. Imagine the documentation to explain that one! Drupal has no intention of getting in the way of that, so yeah, "not supported" is probably fair
    – Clive
    Commented May 5 at 19:06
  • 1
    I agree, believe me I don't find it a particularly elegant implementation overall. At least it's only these :root variables that need the important property. The actual content inside the wysiwyg can be styled per normal by targeting ".ck-content". W3C docs say no space is necessary before the ! delimiter w3.org/TR/css-syntax-3/#consume-a-declaration, but I agree, I typically would add space there as well for legibility and updated the answer!
    – John
    Commented May 5 at 20:27
  • 1
    Weird, MDN says it requires a space: The !important comes after the value of the property value pair declaration, preceded by at least one space. Wonder why they’d make that up; maybe they’re trying to sneak some code style in there :)
    – Clive
    Commented May 6 at 12:16
1

So, the question is: as the CKEditor 5 is part of the Drupal core, what's the proper Drupal-way to override its css variables to customize the editor? Is there a Drupal documentation addressing this which I missed?

The Drupal implementation, documentation, and knowledge of other people addressing this question suggest that this CKEditor 5 feature is not supported by the Drupal 10 integration.

So the only option is some kind of workaround. There are following possibilities (not exhaustive list):

  • Override styles using the !important declaration. See this answer for more details.
  • Provide styles with JavaScript, just like the CKEditor 5 itself. See below.

Provide styles with JavaScript

Theme

In the theme module (using "my_theme" in examples below), add/update following files.

Create the js/components/ckeditor-config.js file:

(function ($, Drupal, drupalSettings) {
    document.write('<style>:root{--ck-color-base-background:#red;--ck-color-base-border:#green;}</style>');
})(jQuery, Drupal, drupalSettings);

Explanation:

  • The script specifies custom style for the CKEditor 5.
  • In the example above, the --ck-color-base-background and --ck-color-base-border variables are set, but others can be defined, too, of course.
  • Any valid name can be used instead of the ckeditor-config.js, of course.

Add to the my_theme.libraries.yml file (create if it doesn't exist):

my_ckeditor5:
  js:
    js/components/ckeditor-config.js: {}

Explanation:

  • Define my_ckeditor5 library (can use any valid name) which references the ckeditor-config.js.
  • This allows other systems/configuration to reference the my_ckeditor5 as referencing js file directly isn't the proper Drupal-way.

Add to the my_theme.info.yml file:

libraries-extend:
  core/ckeditor5.editorClassic:
    - my_theme/my_ckeditor5

Explanation:

  • Hook the my_ckeditor5 library to the core/ckeditor5.editorClassic (a library provided by the Drupal.) That means when something includes it to a page (e.g. node editor), the my_ckeditor5 will be appended. That means:
  • Whenever the CKEditor 5 is used, the custom style library will be included, too.
  • Hooking to the core/ckeditor5.editorClassic assures that style variables are overridden after they're specified by the CKEditor 5 and before the editor is created.

Generating the Script with Styles

Specifying styles in a JavaScript file is kind of cumbersome. Additionally, a project might be using a CSS generator (like lesscss.) For these reasons, it might be viable to specify styles in a separate file and generate the script.

For example, following Python script can be integrated to site buildings process:

#!/usr/bin/env python3

import re
import sys


if len(sys.argv) != 3:
    print('Expected arguments: {0} <input-css-file> <output-js-file>'
        .format(sys.argv[0]))

    sys.exit(1)


inCssFilePath = sys.argv[1]
outJsFilePath = sys.argv[2]


with open(inCssFilePath, 'r') as inCssFile:
    cssContent = inCssFile.read()

# Remove all white spaces and apostrophes to a) squash all lines (otherwise the
# generated JS would be invalid), b) to minimize the length of the CSS (JS
# minimizer obviously wouldn't do that), and c) to avoid breaking JS.
pattern = re.compile(r"['\s]+")
cssContent = re.sub(pattern, '', cssContent)


jsTemplate = """//
// Auto-generated CKEditor script with overridden theme variables.
//

(function ($, Drupal, drupalSettings) {
    document.write('<style>{css}</style>');
})(jQuery, Drupal, drupalSettings);
"""

with open(outJsFilePath, 'w') as outJsFile:

    jsParts = jsTemplate.split('{css}', 2)

    outJsFile.write(jsParts[0])
    outJsFile.write(cssContent)
    outJsFile.write(jsParts[1])

Usage: pass two arguments: name of the input css file (must exist) and output js file (will be overwritten if it exists.)

Notes

  • This approach uses some implementation details, so it might be needed to modified when Drupal is updated.
  • Parsing JavaScript file has some overhead, so the script file should define necessary minimum, i.e. set CKEditor 5 variables, nothing more. It's definitely recommended to specify other styles in standard css files as usual.
  • Disclaimer: I'm just a beginner in Drupal, so I might missed some cases when the approach above isn't enough. For example, there might be better library than the core/ckeditor5.editorClassic to hook the custom script or maybe it's needed to hook to multiple libraries.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.