Skip to content

CKEditor: fix transparent/unreadable editor with a --ck-* variable theme - #27

Open
tanhtm wants to merge 2 commits into
main2.0from
fix/ckeditor-css
Open

tanhtm wants to merge 2 commits into
main2.0from
fix/ckeditor-css

Conversation

@tanhtm

@tanhtm tanhtm commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem

CKEDITOR_5_CUSTOM_CSS = 'base/css/ckeditor5.css' is linked as-is by django-ckeditor-5, but the file was Tailwind source that no build step compiled:

  • browsers dropped @apply: 100 of its 108 rules ended up empty
  • theme('colors.*') is not CSS, so the --ck-color-* overrides were invalid: the editor and toolbar became transparent, and in dark mode the text stayed black (CKEditor's --ck-content-font-color: #000) on the dark admin background

This affects projects that use text_field_widget = 'ckeditor' or CKEditor5Widget.

Fix

base/css/ckeditor5.css is now plain CSS with no build step. It only overrides CKEditor's --ck-* variables, which is CKEditor's documented theming method, so every feature keeps CKEditor's own styles.

  • Light: borders, text, focus, links and "on" buttons use Unfold's --color-base-*, --color-primary-* and --color-font-* colors. Each var() falls back to CKEditor's default.
  • Dark (Unfold's dark class on <html>, also set by daisyUI dark themes):
    • the base variables, from which most of CKEditor's UI colors derive
    • every light default that isn't derived from them: split-button hover, disabled inputs and buttons, placeholders, blur selection, widget borders, links, shadows, captions, and --ck-content-font-color
  • One rule: dark-mode pre, whose #353535 text color has no variable.
  • Test: fails if Tailwind syntax (@apply, theme(, @custom-variant, @tailwind) reaches the served file. It fails on the old file.
  • docs/CKEDITOR5_CUSTOMIZATION.md: rewritten. It described a build step and a JS file that don't exist.

An earlier commit in this PR compiled the old @apply rules instead. The review showed that broke CKEditor features, so it is reverted.

Verified

  • In Chrome, with Unfold's CSS, styles.css, CKEditor's CSS and Unfold's injected colors, using the package's own toolbar config:
    • light and dark: opaque, readable editor and toolbar
    • the colour grid, highlight colours, image-style-side, nested todos, code blocks and table backgrounds all behave as in stock CKEditor
  • The compiled styles.css is unchanged vs main2.0.
  • manage.py test whiteneuron: 74 tests pass.

Not changed

These were already broken on main2.0 and are left for a separate change: Unfold's preflight resets content typography (headings at body size, hidden list bullets and todo checkboxes).

🤖 Generated with Claude Code

django-ckeditor-5 links CKEDITOR_5_CUSTOM_CSS as-is, but
base/css/ckeditor5.css was Tailwind source that was never built:
browsers dropped every @apply rule (100 of 108 rules were empty) and the
theme('colors.*') values made the --ck-color-* variables invalid, so the
editor and toolbar backgrounds were transparent and the text stayed black
on the dark admin background.

- the source moves to ./ckeditor5.css; scripts/tailwind.sh builds it into
  the same static path, so projects need no settings change
- Tailwind 4: v3-only utilities converted (ring, shadow-sm, rounded,
  outline-none, *-opacity-*), scrollbar plugin classes dropped, theme()
  replaced by --theme() / var()
- dark mode follows unfold's "dark" class like styles.css, instead of the
  OS setting (prefers-color-scheme) or only data-theme="dark", so the
  light admin theme stays light and daisyUI dark themes are covered
- unfold's base/primary colors are referenced, not redefined
- --ck-color-base-border set too (the toolbar panel kept CKEditor's light
  #ccced1 line in dark mode)
- CI checks the built file is committed; a test fails if source syntax
  reaches the served file again

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@tanhtm tanhtm left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review of the compiled CKEditor CSS.

TL;DR: compiling ckeditor5.css fixes the transparent/unreadable editor, but it also switches on ~100 dormant @apply rules for the first time. Several of them break toolbar features we enable: colour pickers, highlight colours, the insert-table grid, image side/align styles, code blocks, nested todos and table backgrounds. Dark mode is also still incomplete (pseudo-element dark: rules compile to an empty :is(), and some --ck-color-* light defaults are not overridden). Details are inline.

Suggestion: fix the bug with a variables-only stylesheet (--ck-color-* overrides for light and dark). That's plain CSS with no build step, so it fixes the readability bug without these side effects.

Not inline (file not in this diff): docs/CKEDITOR5_CUSTOMIZATION.md (~L208) still says whiteneuron/static/base/css/ckeditor5.css is the @apply source and tells users to run npx tailwindcss -i ./static/base/css/ckeditor5.css …. That file is now minified build output. Anyone who edits it will have their change overwritten by scripts/tailwind.sh, and CI's "Committed files match the build" step will fail. The doc should point at the root ckeditor5.css.

Comment thread ckeditor5.css Outdated
}

/* Color picker styling */
.ck.ck-color-selector {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Colour selector squeezed into 1/6 of the panel. grid grid-cols-6 lands on .ck.ck-color-selector, whose children are the colour-grids fragment and the colour-picker fragment, not the tiles. Font colour, background colour and the table/cell colour inputs get a dropdown ~6x wider with the palette in its left sixth. This rule was dead before (uncompiled @apply), so it's a new regression. Suggest dropping it; CKEditor already lays out .ck-color-grid itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. Checked in Chrome: the font colour panel is CKEditor's normal 5-column grid.

Comment thread ckeditor5.css Outdated
}

/* Highlight styling */
.ck.ck-content mark {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All highlights render yellow. .ck.ck-content mark (0,2,1) beats CKEditor's .ck-content .marker-green / .pen-red (0,2,0). Picking 'Green marker' or 'Red pen' saves <mark class="marker-green"> but the editor shows yellow-200 with gray-900 text, so the editor no longer matches the saved content. Scope it to mark:not([class]) or drop it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. Checked: marker-green and pen-red render their own colours.

Comment thread ckeditor5.css Outdated
}

/* Buttons styling refined */
.ck.ck-button {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global button sizing hits every non-toolbar button. This now loads after CKEditor's CSS at the same specificity, so h-8 min-h-8 px-2.5 py-1.5, the transparent border and justify-center apply everywhere:

  • the insert-table grid boxes become 32px cells with invisible borders
  • colour tiles become 24x32 with padding
  • heading/code-block/font-size list items get centred labels and check marks

Suggest scoping to .ck.ck-toolbar .ck.ck-button, or theming via --ck-* variables.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. Button sizing, the table grid and colour tiles are CKEditor's defaults again.

Comment thread ckeditor5.css Outdated
}

/* Image widget styling */
.ck.ck-content figure.image {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaks imageStyle:side / alignLeft / alignRight. figure.image { mx-auto my-4 max-w-full } (0,3,1) beats .ck-content .image.image-style-side { max-width:50% } and the align-left/right margins (0,3,0). Side images float at 100% width so text can't wrap, and left/right-aligned images lose their gap to the text. Suggest figure.image:not([class*="image-style-"]) or removing max-w-full/mx-auto here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. Checked: image-style-side is max-width: 50%, floated right, and text wraps.

Comment thread ckeditor5.css Outdated
@apply underline transition-colors duration-200;
}

.ck.ck-content code {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline-code style leaks into code blocks. .ck.ck-content code (0,2,1) also matches pre > code and beats CKEditor's .ck-content pre code { background:unset; padding:0 } (0,1,2). In light mode, every line of a codeBlock shows as a light gray-100 box inside the dark <pre>, and the first line gets indented. Use :not(pre) > code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. Checked: pre code has a transparent background. In dark mode, one rule recolours pre text, because CKEditor hard-codes #353535 there with no variable.

Comment thread scripts/tailwind.sh Outdated

npx @tailwindcss/cli -i styles.css -o whiteneuron/static/base/css/styles.css --minify
# CKEDITOR_5_CUSTOM_CSS: served as-is by django-ckeditor-5, so its @apply must be compiled here
npx @tailwindcss/cli -i ckeditor5.css -o whiteneuron/static/base/css/ckeditor5.css --minify

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach. The bug (transparent editor, black text in dark mode) only needs valid --ck-color-* overrides. That is plain CSS, it's CKEditor's documented theming method, and it needs no build step or CI gate. Compiling here switches on ~100 Tailwind-3 @apply rules that have never rendered against CKEditor 47's DOM. That causes the regressions flagged in ckeditor5.css (colour selector, highlights, buttons, image styles, code blocks, todo lists, tables). It also silently changes behaviour: a 32rem height cap with internal scrolling, and a 1px border shift on focus. I'd consider shipping a variables-only stylesheet instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, adopted in 5543eb7. base/css/ckeditor5.css is now plain CSS that only overrides --ck-* variables (light + dark), with CKEditor's default as each var() fallback. The Tailwind source, the scripts/tailwind.sh step, the styles.css exclusions and the CI gate are all removed. No 32rem height cap, no focus border shift.

Comment thread whiteneuron/base/tests_regressions.py Outdated
from django.conf import settings
from django.contrib.staticfiles import finders

with open(finders.find(settings.CKEDITOR_5_CUSTOM_CSS), encoding="utf-8") as f:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This resolves the file through settings.CKEDITOR_5_CUSTOM_CSS. If a project overrides it, the test checks that project's file instead of the packaged one, and if it's None it crashes with a TypeError. Use finders.find("base/css/ckeditor5.css"). Also:

  • .ck.ck-editor{ only matches minified output; ".ck.ck-editor" is enough.
  • No DB is needed, so SimpleTestCase avoids the per-test transaction.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: SimpleTestCase, the file found via finders.find("base/css/ckeditor5.css"), and a --ck-color-base-background presence check that doesn't depend on minified output.

Comment thread ckeditor5.css Outdated
@apply focus:outline-hidden focus:ring-2 focus:ring-primary-500/40 dark:focus:ring-primary-500/30;
}

.ck.ck-button:hover {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate/conflicting rules. Later copies silently override earlier ones:

  • .ck.ck-button:hover is set twice (gray-700/70 vs gray-700)
  • .ck.ck-content:focus appears at L165 and L433
  • td/th borders are set at L150 and again at L241 with other colours
  • the toolbar background is set both via --ck-color-toolbar-background and .ck.ck-toolbar bg-base-100
  • .ck-color-selector__option (L314) doesn't exist in CKEditor 47

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: the PR now ships a variables-only stylesheet, so this rule is gone and CKEditor's own style applies. The file is now about 80 lines of variables, with no duplicated selectors.

Comment thread ckeditor5.css Outdated
@custom-variant dark (&:is(.dark, .dark *, [data-theme=dark], [data-theme=dark] *));

/* unfold defines these at runtime (UNFOLD["COLORS"], daisy-themes.css): reference, don't redefine */
@theme inline reference {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the dark @custom-variant and the --color-base-* / --color-primary-* @theme reference mapping are copied from styles.css. A shared partial @imported by both builds would stop the editor drifting if the dark selector or the unfold colour mapping changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: there's no second Tailwind build any more, so nothing is copied from styles.css. The stylesheet references unfold's --color-* variables directly.

Comment thread ckeditor5.css Outdated
/* CKEditor 5 styles for Unfold (settings.CKEDITOR_5_CUSTOM_CSS).
Source only: scripts/tailwind.sh builds it into whiteneuron/static/base/css/ckeditor5.css,
the file browsers get (they ignore @apply and --theme()). */
@layer theme;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this build ships its own @layer properties, @layer theme (:root variables) and Tailwind 4.1.16 @property --tw-* registrations, all of which unfold (Tailwind 4.3.3) and styles.css already load on the same page. That's 42.7 KB of output from ~20 KB of source. Because this file loads last, its @property/theme values win page-wide, which couples the whole admin to whichever Tailwind version built this file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5543eb7: no @layer, @property or theme variables any more. The file only sets --ck-* variables (plus one pre rule), so it can't override unfold's Tailwind globals.

Review on #27: compiling the old @apply rules switched on ~100 rules that
had never rendered against CKEditor 47 and broke the colour selector,
highlight colours, the insert-table grid, image side/align styles, code
blocks, nested todos and table backgrounds; pseudo-element dark: rules
compiled to an empty :is().

The bug only needed valid variables, so base/css/ckeditor5.css is now
plain CSS that overrides CKEditor's --ck-* variables (its documented
theming method) and leaves every feature on CKEditor's own styles:

- light: borders, text, focus, links and "on" buttons from unfold's
  base/primary/font colors, each with CKEditor's default as fallback
- dark (unfold's "dark" class): base variables plus every light default
  that is not derived from them: split-button hover, input/button
  disabled states, placeholders, blur selection, widget borders, links,
  shadows, captions, and --ck-content-font-color (#000, the black text)
- one rule for code blocks, whose #353535 text has no variable

The Tailwind source, build step, styles.css exclusions and CI gate from
the previous commit are removed. The test uses SimpleTestCase and finds
the packaged file by path, not via settings. docs/CKEDITOR5_CUSTOMIZATION.md
is rewritten: it described an @apply build and a JS file that don't exist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tanhtm

tanhtm commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Switched to the variables-only approach in 5543eb7. Replies are inline. On the points not inline:

  • docs/CKEDITOR5_CUSTOMIZATION.md: rewritten. It described an @apply build, a ckeditor5-unfold.js, and APIs (initUnfoldCKEditor, auto-save, debug mode) that don't exist in the repo. It now documents the real setup: text_field_widget = "ckeditor", CKEDITOR_5_CUSTOM_CSS, the "plain CSS, no build" rule, and how a project overrides --ck-* variables.

Checked in Chrome with unfold's CSS, styles.css, CKEditor's CSS and unfold's injected colours, using the package's own toolbar config:

  • light and dark: the editor is opaque and the text readable
  • the colour panel is a normal grid
  • marker-green and pen-red keep their colours
  • side images are 50% wide and floated
  • todos are list items
  • pre code is transparent
  • cell backgrounds are transparent
  • the compiled styles.css is unchanged vs main2.0, and all 74 tests pass

Not changed (already on main2.0, where the old file had no effect): unfold's preflight resets the editor's content typography. Headings render at body size, list bullets and todo checkboxes are hidden. Restoring them would mean content styles (.ck-content h1, …), which is a separate change.

@tanhtm tanhtm changed the title CKEditor: compile the custom CSS instead of serving Tailwind source CKEditor: fix transparent/unreadable editor with a --ck-* variable theme Oct 19, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant