Generating ckeditor5-stylesheets for a Radix subtheme

Created on 12 September 2023, over 1 year ago

Problem/Motivation

Document how to see your theme's frontend styles in CKEditor 5 for Drupal 9, 10, and beyond.

Proposed resolution

Radix comes with tooling (Laravel Mix) which supports PostCss that we can leverage to automate CKE5-compatible stylesheet creation.

Remaining tasks

Document it!

User interface changes

None.

API changes

Following the guide here, your Radix theme can have frontend styles in the CKEditor5 on frontend or backend.

Data model changes

None.

πŸ“Œ Task
Status

Active

Version

5.1

Component

Documentation

Created by

πŸ‡ͺπŸ‡¨Ecuador jwilson3

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @jwilson3
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    This guide should follow the basic setup required for CKEditor 5 stylesheet definition in Drupal themes.

    Resources:

    Change Record: New API for adding theme-specific styles in CKEditor 5 β†’

    How to style custom content in CKEditor 5 β†’

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    I'm going to write the initial documentation from the Radix 8.x-4.x (Bootstrap 4) subtheme perspective, because that is what the current project I'm updating to Drupal 10 uses. I then suggest someone work through the same steps for a Radix 5 (Bootstrap 5) subtheme, to verify that the steps are the same, or what needs modification/changing.

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    Summary / tl,dr;

    Radix 4 comes with Laravel Mix as an npm dependency. Laravel Mix supports both SASS and PostCSS, which we can leverage to A) wrap all CSS rules in the required .ck-content selector and B) convert selectors related to the body and html tags to work inside CKE5.

    Part A: Wrap all CSS rules in .ck-content

    1. Create a new SCSS stylesheet at the top-level of your subtheme's scss folder called src/sass/ckeditor5.style.scss with the following contents:
      // in src/sass/ckeditor5.style.scss
      .ck-content {
        @import 'myradixsubtheme.style';
      }
      
    2. Let Laravel Mix know about your new stylesheet.

      Add 'src/sass/ckeditor5.style.scss' after the main subtheme.style.scss entry in file webpack.mix.js:

      // in webpack.mix.js
      mix.sass('src/sass/ckeditor5.style.scss', 'css', sass_options);
      
    3. Compile your theme, as usual.
      npm run watch
      

      Then, load the generated CSS file src/sass/ckeditor5.style.scss in your code editor and confirm that all selectors have been wrapped in .ck-content.

    4. Add ckeditor5-stylesheets rule to your subtheme's info.yml
      # in myradixsubtheme.info.yml
      ckeditor5-stylesheets:
        - assets/css/ckeditor5.style.css
      

    Note: You may be able to stop here. However, most sub-themes will define their base font sizing and font-family information in an html or body selector. But these selectors will be incorrectly prefixed with .ck-content body or .ck-content html, neither of which will be recognized or applied inside the Rich Text Editor. In this case, proceed with part B below.

    Part B: update body and html tag selectors for CKE5-compatibility

    For this part, we can leverage the PostCSS integration in Laravel Mix to execute code cleanup tasks.

    1. Add postcss-selector-replace as an npm dependency.
      npm install postcss-selector-replace  #or
      yarn install postcss-selector-replace
      
    2. Post-process the CSS file with Laravel Mix.

      In file webpack.mix.js, add a postCss processing rule for 'assets/css/ckeditor5.style.css' after the line for src/sass/ckeditor5.style.scss. Provide the options for the postcss-selector-replace to replace .ck-content body and .ck-content html with just .ck-content.

      /* in webpack.mix.js */
      mix.sass('src/sass/ckeditor5.style.scss', 'css', sass_options);
      mix.postCss('assets/css/ckeditor5.style.css', 'css',  [
        require("postcss-selector-replace")({
          before: [".ck-content body", ".ck-content html"],
          after: [".ck-content", ".ck-content"]
        })
      ]);
      
    3. Compile your theme, as usual.
      npm run watch
      

      Load the assets/css/ckeditor5.style.css file in your code editor and confirm that the appropriate selectors have been converted .ck-content.

    Part C: webfonts

    Themes that host their own webfonts using @font-face rules in their SASS code need not apply here as things should already just work. However, if you're using Google Fonts or another third party, there may be more work for you to load in your webfonts for CKEditor5 on the backend theme. This can be done in several ways and this is mostly outside the scope of this tutorial. For example, you could maintain a separate stylesheet with the @font-face webfont declarations and add it to ckeditor5-stylesheets in your info.yml or simply add the @import rule for the google font url to the top of your src/sass/ckeditor5.style.scss file.

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    Feedback welcome. I'm at a loss for editability of my comment above.

    Sadly Radix docs are not hosted on Drupal.org

    Should I just move the comment HTML into the issue summary here so others can edit and improve upon it?

  • Damn James, great work. I love what I see, I'll have to circle back on this later and respond, but great start πŸ‘

  • πŸ‡²πŸ‡ΎMalaysia ckng

    Good example if you want full control over the CKEditor styling.
    However, a simpler way would be to just point `ckeditor5-stylesheets` to the theme generated `build/css/main.style.css`. So CKEditor style will follow theme look and feel.

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    However, a simpler way would be to just point `ckeditor5-stylesheets` to the theme generated `build/css/main.style.css`

    This is sometimes fine, but often doesn't work as intended because CKEditor 5 styles "bleed" into the Admin theme, and can cause style colisions for things like basic style tags, fonts, Headings, Lists, etc, etc. This is all well summarized in the documentation notes and screenshots on this page: https://www.drupal.org/docs/core-modules-and-themes/core-modules/ckedito... β†’

  • πŸ‡ΊπŸ‡ΈUnited States joshuami Portland, OR

    @jwilson3, great write up! The only catch that I see is that the way the Radix subtheme pulls in the Bootstrap Sass files, the root.scss files would be prefixed with .ck-content, which can cause the variables in that file to no longer render. So all the color and size overrides that Bootstrap offers, or the variables that are overridden would not appear in CKEditor.

    To get around this in one of my Radix themes, I restructured the Radix way of pulling in Sass quite a bit so that the subtheme's myradixsubtheme.style.scsss and ckeditor5.style.scss both pull in those root variables without the .ck-content prefix. So the ckeditor5.style.scss would look a little like this:

    // ------------------------------------------------------------------------------------------
    // Subtheme CSS overrides for CKEditor 5
    //
    // This stylesheet is applied to admin pages with a CKEditor 5 WYSIWYG editor. Use it to set
    // or override styles of content within the editor.
    // ------------------------------------------------------------------------------------------
    
    // ------------------------------------------------------------------------------------------
    // Import all the functions and variables.
    
    // 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
    
    @import '~bootstrap/scss/functions';
    
    // 2. Include any default variable overrides here
    @import 'base/variables';
    
    // 3. Include remainder of required Bootstrap stylesheets
    @import '~bootstrap/scss/variables';
    
    // 4. Include any default map overrides here
    // We don't have any in Westy
    
    // 5. Include remainder of required parts
    @import '~bootstrap/scss/maps';
    @import '~bootstrap/scss/mixins';
    @import '~bootstrap/scss/root';
    @import '~bootstrap/scss/utilities';
    
    // ------------------------------------------------------------------------------------------
    // Preface all Subtheme styles with .ck-content to restrict them to the content within CKEditor
    .ck-content {
    
      // 6. Optionally include any other parts as needed
      // We've chosen to comment out includes not in use.
      @import '~bootstrap/scss/reboot';
      @import '~bootstrap/scss/type';
      @import '~bootstrap/scss/images';
      @import '~bootstrap/scss/containers';
      @import '~bootstrap/scss/grid';
      @import '~bootstrap/scss/tables';
      @import '~bootstrap/scss/forms';
      @import '~bootstrap/scss/buttons';
      @import '~bootstrap/scss/transitions';
      @import '~bootstrap/scss/dropdown';
      @import '~bootstrap/scss/button-group';
      @import '~bootstrap/scss/nav';
      @import '~bootstrap/scss/navbar'; // Requires nav
      @import '~bootstrap/scss/card';
      @import '~bootstrap/scss/breadcrumb';
      @import '~bootstrap/scss/accordion';
      @import '~bootstrap/scss/pagination';
      @import '~bootstrap/scss/badge';
      @import '~bootstrap/scss/alert';
      // @import '~bootstrap/scss/progress';
      @import '~bootstrap/scss/list-group';
      @import '~bootstrap/scss/close';
      // @import '~bootstrap/scss/toasts';
      // @import '~bootstrap/scss/modal'; // Requires transitions
      // @import '~bootstrap/scss/tooltip';
      // @import '~bootstrap/scss/popover';
      // @import '~bootstrap/scss/carousel';
      // @import '~bootstrap/scss/spinners';
      // @import '~bootstrap/scss/offcanvas'; // Requires transitions
      // @import '~bootstrap/scss/placeholders';
      @import "~bootstrap/scss/helpers";
    
      // Subtheme Base
      // ------------------------------------------------------------------------------
      @import 'base/helpers';
      @import 'base/elements';
      @import 'base/typography';
    
      // Subtheme Layouts
      // -----------------------------------------------------------------------------
      @import "layout/page.header";
      @import "layout/page.content";
      @import "layout/page.footer";
    
      // Subthteme Components
      // -----------------------------------------------------------------------------
      @import "../components/nav/nav";
      @import "components/accordions";
      @import "components/alerts";
      @import "components/badges";
      @import "components/buttons";
      @import "components/cards";
      @import "components/callouts";
      @import "components/form";
      @import "components/item-list";
      @import "components/list";
      @import "components/navs";
      @import "components/office-hours";
      @import "components/pagination";
    }
    
    // 7. Optionally include utilities API last to generate classes based on the
    // Sass map in `_utilities.scss`
    @import '~bootstrap/scss/utilities/api';
    
    

    My myradixsubtheme.style.scsss, looks the same as above, but without the prefix section. I bet there is a better way to do that so that I'm not maintaining two nearly identical *.style.scss files. πŸ€”

    @ckng, if you use your subtheme's built CSS without any prefixes, and you use the Claro admin theme, you will likely see some conflicts between Bootstrap and Claro because they share a couple of classes. This can cause the form to have some weird layout. It also means that your font in Claro will match your frontendβ€”that might not be an issue depending on your font choices.

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    Okay. Great feedback on #10. I guess things are more complex for Radix 5 than for Radix 4, which I dont believe uses any :root or CSS variables. (I could be wrong, but at least the ckeditor5.style.css for my specific myradixsubtheme doesn't have any :root declarations).

    It looks like we might have to have different instructions (or a specific additional "Part E" section for Radix 5.

  • πŸ‡ΊπŸ‡ΈUnited States joshuami Portland, OR

    Oh, I didn't think about v4. I've been so focused on Radix 5 and Bootstrap 5 for the last couple of builds that I forgot what it used to look like. πŸ˜†

    Interestingly, Bootstrap 4.6 does have root variables, but looking at https://git.drupalcode.org/project/radix/-/blob/8.x-4.x/src/kits/default..., I don't see the _root.scss getting called. I think that might be a miss in Radix 4. πŸ€”

    In addition to documentation, it might be worth adding a CKEditor5 stylesheet to Radix and refactoring the way the dependencies are called in to allow the root variables.

    I really think most themes (or at least generated subthemes) should come with a ckeditor5-stylesheet built in now. It would certainly make it easier for people to understand how that particular functionality works.

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    In addition to documentation, it might be worth adding a CKEditor5 stylesheet to Radix and refactoring the way the dependencies are called in to allow the root variables.

    I really think most themes (or at least generated subthemes) should come with a ckeditor5-stylesheet built in now. It would certainly make it easier for people to understand how that particular functionality works.

    Totally agree, and if there isn't already a follow-up ticket to implement that for Radix I suggest someone add one ASAP!

    This page came out of my own need to upgrade a Drupal 9 site with Radix 4 to Drupal 10 (which I think most people are now doing, given the impending D9 EOL). Happy to have the additional feedback about Radix 5. Sadly, a migration of these projects to Bootstrap/Radix 5 is not in the cards for these maintenance projects.

  • πŸ‡³πŸ‡ΏNew Zealand ericgsmith

    Fantastic contribution - thank you for sharing.

    Followed the steps on a site using a Radix 4 sub theme and worked as expected. Thank you for putting this together!

  • πŸ‡©πŸ‡ͺGermany marcoka

    DId not work for me:
    [webpack-cli] ReferenceError: sass_options is not defined

  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    Rereading through this again for another project that is about to add ckeditor5 styles from a bootstrap-based theme.

    Picking up on comment #10:

    the root.scss files would be prefixed with .ck-content, which can cause the variables in that file to no longer render.

    I've only now just realized that the PostCSS code in #4.B can handle this:


    mix.sass('src/sass/ckeditor5.style.scss', 'css');
    mix.postCss('assets/css/ckeditor5.style.css', 'css', [
    require("postcss-selector-replace")({
    before: [".ck-content body", ".ck-content html", ".ck-content :root"],
    after: [".ck-content", ".ck-content", ".ck-content"]
    })
    ]);

  • Changing the version to 6.x to be added to the official Radix docs

  • Okay with Radix 6 it needs another write up/update I believe, anyone up for it to help out?

  • I'm marking this as closed, if anyone wants to help out with a new documentation that we can share for Radix 6.x I'd be glad to add it to our official docs. for now marking this as done

  • Status changed to Fixed 3 months ago
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024