diff --git a/docs/CKEDITOR5_CUSTOMIZATION.md b/docs/CKEDITOR5_CUSTOMIZATION.md
index c3613e3..0346ff8 100644
--- a/docs/CKEDITOR5_CUSTOMIZATION.md
+++ b/docs/CKEDITOR5_CUSTOMIZATION.md
@@ -1,247 +1,89 @@
-# CKEditor5 Unfold Theme Integration
+# CKEditor 5 in the Unfold admin
-This implementation provides a comprehensive customization of CKEditor5 to seamlessly integrate with the Unfold admin theme, utilizing Tailwind CSS 3 and DaisyUI 4 with the `ui-` prefix.
+django-whiteneuron ships [django-ckeditor-5](https://github.com/hvlads/django-ckeditor-5) configured
+for the admin, plus a small stylesheet that themes the editor with Unfold's colors in light and dark
+mode.
-## Features
+## Enabling the editor
-### 🎨 **Visual Design**
-- **Modern UI**: Clean, minimalist design matching Unfold's aesthetic
-- **Dark Mode Support**: Automatic theme detection and switching
-- **Responsive Design**: Mobile-first approach with adaptive toolbars
-- **DaisyUI Integration**: Uses `ui-` prefixed components for consistency
+The default `TextField` widget of `whiteneuron.base.modeladmin.ModelAdmin` is `wysiwyg`. To use
+CKEditor in a model admin:
-### 🛠**Enhanced Functionality**
-- **Auto-save**: Automatic content preservation with localStorage
-- **Accessibility**: WCAG 2.1 compliant with proper ARIA labels
-- **Word Count**: Real-time statistics display
-- **Responsive Toolbar**: Simplified mobile interface
-- **Theme Synchronization**: Automatic dark/light mode switching
-
-### 🎯 **User Experience**
-- **Smooth Animations**: Subtle transitions and hover effects
-- **Focus Management**: Enhanced keyboard navigation
-- **Error States**: Visual feedback for form validation
-- **Loading States**: Progressive enhancement indicators
-
-## File Structure
-
-```
-whiteneuron/static/base/
-├── css/
-│ └── ckeditor5.css # Main styling with Tailwind classes
-└── js/
- └── ckeditor5-unfold.js # Enhanced functionality and configuration
-```
-
-## Implementation Details
-
-### CSS Architecture (`ckeditor5.css`)
-
-The CSS file uses Tailwind's `@apply` directive to leverage the existing design system:
-
-- **Container Styling**: Matches Django form field appearance
-- **Toolbar Design**: Uses DaisyUI button components with `ui-` prefix
-- **Content Area**: Styled with typography utilities
-- **Dark Mode**: Automatic color scheme adaptation
-- **Responsive Breakpoints**: Mobile-optimized layouts
-
-### JavaScript Enhancements (`ckeditor5-unfold.js`)
-
-The JavaScript file provides advanced functionality:
-
-- **Theme Management**: Detects and responds to theme changes
-- **Configuration**: Comprehensive CKEditor setup optimized for content editing
-- **Auto-save**: Background content preservation
-- **Accessibility**: Enhanced keyboard and screen reader support
-- **Mobile Optimization**: Responsive toolbar configuration
-
-## Configuration Options
-
-### Default Toolbar Configuration
-
-```javascript
-toolbar: {
- items: [
- 'heading', '|',
- 'bold', 'italic', 'link', '|',
- 'bulletedList', 'numberedList', 'blockQuote', '|',
- 'uploadImage', 'insertTable', '|',
- 'code', 'codeBlock', '|',
- 'subscript', 'superscript', 'highlight', '|',
- 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', '|',
- 'mediaEmbed', 'removeFormat', '|',
- 'sourceEditing'
- ]
-}
+```python
+class ArticleAdmin(ModelAdmin):
+ text_field_widget = "ckeditor" # options: 'wysiwyg', 'ckeditor', 'mdeditor', ''
```
-### Color Palette Integration
-
-The color picker integrates with your theme's primary colors:
-
-```javascript
-fontColor: {
- colors: [
- // Standard colors
- { color: 'hsl(0, 0%, 0%)', label: 'Black' },
- // ... other colors
- // Theme integration
- { color: 'rgb(var(--color-sky-500))', label: 'Primary' },
- { color: 'rgb(var(--color-sky-600))', label: 'Primary Dark' },
- { color: 'rgb(var(--color-sky-400))', label: 'Primary Light' }
- ]
-}
-```
-
-## Usage
-
-### Basic Implementation
+Or use `django_ckeditor_5.fields.CKEditor5Field` / `django_ckeditor_5.widgets.CKEditor5Widget` directly.
-```html
-
-
-
+The package settings (`whiteneuron/base/settings.py`) define `CKEDITOR_5_CONFIGS` (toolbars),
+`CKEDITOR_5_FILE_STORAGE`, `CKEDITOR_5_FILE_UPLOAD_PERMISSION` and:
-
-
-
-
+```python
+CKEDITOR_5_CUSTOM_CSS = "base/css/ckeditor5.css"
```
-### Django Template Integration
+django-ckeditor-5 adds that file to the widget's media after CKEditor's own CSS.
-```html
-{% load static %}
+## The theme stylesheet
-
-
-
- {{ form.content }}
-
-
+`whiteneuron/static/base/css/ckeditor5.css` is **plain CSS with no build step**. Browsers get the
+file exactly as committed, so it must not contain Tailwind syntax (`@apply`, `theme()`,
+`@custom-variant`); a regression test checks this.
-
-
-
-```
-
-### Custom Configuration
-
-```javascript
-// Initialize with custom configuration
-window.initUnfoldCKEditor(document.querySelector('#my-editor'), {
- toolbar: ['bold', 'italic', 'link'],
- placeholder: 'Custom placeholder text...',
- // Other custom options
-});
-```
-
-## Customization
+It only overrides CKEditor's `--ck-*` CSS variables, which is
+[CKEditor's documented theming method](https://ckeditor.com/docs/ckeditor5/latest/framework/deep-dive/ui/theme-customization.html).
+Layout, sizes and feature styles (colour pickers, highlights, image styles, tables, code blocks,
+todo lists) stay CKEditor's own.
-### Adding New Toolbar Items
+- **Colors** come from Unfold: `--color-base-*`, `--color-primary-*` and `--color-font-*`
+ (`UNFOLD["COLORS"]`, remapped per theme by `daisy-themes.css`). The `var()` fallbacks only apply
+ without them: CKEditor's defaults in light mode, Tailwind grays in dark mode.
+- **Content font** is Inter, Unfold's font.
+- **Dark mode** follows Unfold's `dark` class on ``, which daisyUI dark themes also set.
+ CKEditor's light defaults that are not derived from its base variables (button and input states,
+ placeholders, selection, content text, captions, panels) are overridden one by one. A regression
+ test lists the ones left as they are (accents, translucent overlays, badges) and fails when a
+ django-ckeditor-5 upgrade adds a new one.
+- **Fixed colors without a variable** get a dark-mode rule each: code block text (`#353535`), todo
+ list checkbox borders (`#333`) and the text of find & replace matches.
-```javascript
-// Extend the default configuration
-window.UnfoldCKEditorConfig.toolbar.items.push('specialCharacters');
-```
+## Customizing in a project
-### Custom Color Themes
+django-ckeditor-5 loads a single `CKEDITOR_5_CUSTOM_CSS` file. To keep the packaged theme and adjust
+it, point the setting at your own static file and import the packaged one first:
-```javascript
-// Add custom colors to the palette
-window.UnfoldCKEditorConfig.fontColor.colors.push({
- color: '#your-color',
- label: 'Custom Color'
-});
+```python
+CKEDITOR_5_CUSTOM_CSS = "myproject/css/ckeditor5.css"
```
-### Styling Overrides
-
```css
-/* Add custom styles in your CSS file */
-.ck.ck-editor.my-custom-editor {
- @apply border-2 border-blue-500;
-}
+/* static/myproject/css/ckeditor5.css */
+@import url("../../base/css/ckeditor5.css");
-.ck.ck-editor.my-custom-editor .ck-toolbar {
- @apply bg-blue-50 dark:bg-blue-900;
+:root {
+ --ck-color-focus-border: #7c3aed;
}
-```
-
-## Browser Support
-
-- **Modern Browsers**: Chrome 63+, Firefox 60+, Safari 12+, Edge 79+
-- **Mobile Support**: iOS Safari 12+, Chrome Mobile 63+
-- **Accessibility**: WCAG 2.1 AA compliant
-- **RTL Support**: Full right-to-left text direction support
-
-## Performance Considerations
-
-- **Lazy Loading**: CKEditor initializes only when needed
-- **Asset Optimization**: Minified CSS and JavaScript
-- **Memory Management**: Proper cleanup on navigation
-- **Auto-save Throttling**: Configurable save intervals
-
-## Accessibility Features
-
-- **Keyboard Navigation**: Full keyboard support for all features
-- **Screen Reader Support**: Comprehensive ARIA labels and descriptions
-- **High Contrast Mode**: Enhanced visibility in high contrast environments
-- **Focus Management**: Proper focus indicators and tab order
-
-## Error Handling
-
-The implementation includes robust error handling:
-
-- **Initialization Errors**: Graceful fallback to plain textarea
-- **Network Issues**: Local storage backup for content
-- **Browser Compatibility**: Feature detection and polyfills
-
-## Development Notes
-### CSS Processing
-
-The CSS file uses Tailwind's `@apply` directive and requires processing through your build pipeline:
-
-```bash
-# Process through Tailwind
-npx tailwindcss -i ./static/base/css/ckeditor5.css -o ./dist/ckeditor5.css
+html.dark {
+ --ck-color-base-background: #0b1020;
+}
```
-### JavaScript Dependencies
+Your rules come after the import and use the same selectors, so they win. Django's manifest
+storages rewrite the relative `@import` to the hashed file name.
-The JavaScript file depends on:
-- CKEditor5 Classic Editor build
-- Modern browser APIs (MutationObserver, localStorage)
-- CSS custom properties support
+CKEditor declares its variables on `:root` and derives most of them from a few base ones
+(`--ck-color-base-background`, `-foreground`, `-border`, `-text`). Override them on `` (`:root`,
+`html.dark`) so the derived variables follow. The full list is in CKEditor's CSS
+(`django_ckeditor_5/static/django_ckeditor_5/dist/styles.css`).
-### Testing Considerations
-
-Test the implementation across:
-- Different screen sizes (mobile, tablet, desktop)
-- Theme switching (light/dark modes)
-- Form validation states
-- Content restoration from auto-save
+If you need Tailwind utilities, compile your stylesheet in your own build and point
+`CKEDITOR_5_CUSTOM_CSS` at the output, never at the source.
## Troubleshooting
-### Common Issues
-
-1. **Styles not applying**: Ensure Tailwind CSS is processing the `@apply` directives
-2. **Theme not switching**: Check that `data-theme` attribute changes are detected
-3. **Auto-save not working**: Verify localStorage availability and permissions
-4. **Mobile toolbar issues**: Check responsive breakpoints and toolbar configuration
-
-### Debug Mode
-
-Enable debug logging by setting:
-
-```javascript
-window.CKEditorDebug = true;
-```
-
-This will log initialization steps and theme changes to the console.
-
-## License
-
-This implementation follows the same license as your Django project and includes integration code compatible with CKEditor5's GPL license.
\ No newline at end of file
+- **Transparent editor or black text in dark mode**: the stylesheet has invalid values, usually
+ Tailwind source served without a build. Check the file in the browser's network tab.
+- **Theme does not switch**: the variables follow the `dark` class on ``. Check that Unfold's
+ theme switch sets it.
diff --git a/whiteneuron/base/tests_regressions.py b/whiteneuron/base/tests_regressions.py
index 96f0009..08e6235 100644
--- a/whiteneuron/base/tests_regressions.py
+++ b/whiteneuron/base/tests_regressions.py
@@ -1,10 +1,12 @@
"""Regression tests for the hardening / performance changes."""
import asyncio
+import re
+from pathlib import Path
from unittest import mock
from django.contrib.admin.sites import AdminSite
from django.contrib.auth import get_user_model
-from django.test import RequestFactory, TestCase, override_settings
+from django.test import RequestFactory, SimpleTestCase, TestCase, override_settings
from django.urls import reverse
from django.utils import translation
@@ -712,3 +714,71 @@ def broken(**kwargs):
with self.settings(MIDDLEWARE=middleware), self.assertLogs("whiteneuron.base.middleware", level="ERROR"):
page = self.client.get(reverse("admin:login"), {"next": "/admin/"})
self.assertEqual(page.status_code, 200)
+
+
+class CKEditorCustomCssTests(SimpleTestCase):
+ # CKEditor's fixed light colors that stay readable in dark mode: accents, translucent overlays,
+ # self-contained badges and tooltips; highlights get dark text from the browser (MarkText)
+ READABLE_IN_DARK = {
+ "--ck-color-base-action", "--ck-color-base-active", "--ck-color-base-active-focus",
+ "--ck-color-base-error", "--ck-color-base-focus", "--ck-color-button-action-active-background",
+ "--ck-color-button-action-disabled-background", "--ck-color-button-action-hover-background",
+ "--ck-color-button-cancel", "--ck-color-button-save", "--ck-color-color-grid-check-icon",
+ "--ck-color-image-upload-icon", "--ck-color-image-upload-icon-background", "--ck-color-light-red",
+ "--ck-color-switch-button-off-background", "--ck-color-switch-button-off-hover-background",
+ "--ck-color-switch-button-on-hover-background", "--ck-color-upload-bar-background",
+ "--ck-color-upload-placeholder-loader", "--ck-color-widget-hover-border",
+ "--ck-html-embed-source-disabled-color", "--ck-show-blocks-border-color",
+ # translucent
+ "--ck-color-focus-disabled-shadow", "--ck-color-focus-error-shadow", "--ck-color-link-fake-selection",
+ "--ck-color-switch-button-inner-shadow", "--ck-color-table-focused-cell-background",
+ "--ck-content-color-mention-background", "--ck-dialog-overlay-background-color",
+ "--ck-table-selected-cell-background",
+ # badges and tooltips with their own background
+ "--ck-color-resizer-tooltip-background", "--ck-color-resizer-tooltip-text",
+ "--ck-evaluation-badge-background", "--ck-evaluation-badge-text-color",
+ "--ck-powered-by-background", "--ck-powered-by-text-color",
+ # highlights: find results get dark text from a rule, markers from the browser
+ "--ck-color-highlight-background", "--ck-content-highlight-marker-blue",
+ "--ck-content-highlight-marker-green", "--ck-content-highlight-marker-pink",
+ "--ck-content-highlight-marker-yellow", "--ck-content-highlight-pen-green",
+ "--ck-content-highlight-pen-red",
+ }
+
+ @staticmethod
+ def _read(package, path):
+ return (Path(package.__file__).parent / path).read_text(encoding="utf-8")
+
+ def setUp(self):
+ import whiteneuron
+
+ # the packaged file itself: staticfiles finders may return a project's file of the same name
+ self.css = self._read(whiteneuron, "static/base/css/ckeditor5.css")
+
+ def test_packaged_css_is_plain_css(self):
+ # django-ckeditor-5 links CKEDITOR_5_CUSTOM_CSS as-is: the file used to be Tailwind source, so
+ # browsers dropped every @apply rule and theme() left the editor transparent (unreadable in dark mode)
+ source_only = (
+ "@apply", "theme(", "@custom-variant", "@tailwind", "@theme", "@utility", "@variant",
+ "@source", "@plugin", "@reference", "--alpha(", "--spacing(",
+ )
+ for token in source_only:
+ self.assertNotIn(token, self.css)
+ self.assertIn("--ck-color-base-background", self.css)
+
+ def test_dark_mode_covers_ckeditor_light_colors(self):
+ # CKEditor's fixed light colors (not derived from --ck-color-base-*) must be overridden for dark
+ # mode or reviewed above, so a django-ckeditor-5 upgrade that adds one fails here
+ import django_ckeditor_5
+
+ ckeditor_css = self._read(django_ckeditor_5, "static/django_ckeditor_5/dist/styles.css")
+ fixed = {
+ name
+ for block in re.findall(r":root\{([^}]*)\}", ckeditor_css)
+ for name, value in re.findall(r"(--ck-[\w-]+):([^;]+)", block)
+ if re.match(r"\s*(#[0-9a-f]{3,8}\b|rgba?\(|hsla?\()", value, re.I) and "var(" not in value
+ }
+ self.assertGreater(len(fixed), 50) # the parse still finds CKEditor's variables
+ dark = re.search(r"html\.dark \{([^}]*)\}", self.css).group(1)
+ overridden = set(re.findall(r"(--ck-[\w-]+):", dark))
+ self.assertEqual(sorted(fixed - overridden - self.READABLE_IN_DARK), [])
diff --git a/whiteneuron/static/base/css/ckeditor5.css b/whiteneuron/static/base/css/ckeditor5.css
index af3e550..1fd0433 100644
--- a/whiteneuron/static/base/css/ckeditor5.css
+++ b/whiteneuron/static/base/css/ckeditor5.css
@@ -1,550 +1,116 @@
-/* CKEditor5 Custom Styles for Unfold Theme with Tailwind CSS 3 & DaisyUI 4 */
+/*
+ * CKEditor 5 theme for Unfold (settings.CKEDITOR_5_CUSTOM_CSS).
+ *
+ * Plain CSS, no build step: django-ckeditor-5 links this file as-is after CKEditor's own CSS.
+ * It only overrides CKEditor's --ck-* variables (its documented theming method), so the editor's
+ * layout and features keep CKEditor's own styles. Colors come from unfold (UNFOLD["COLORS"],
+ * daisy-themes.css); the var() fallbacks only apply without them: CKEditor's defaults in light
+ * mode, Tailwind grays in dark mode.
+ *
+ * CKEditor declares its variables on :root and derives most of them from a few base ones
+ * (--ck-color-base-*), so overriding those on recolors the whole UI. The others are
+ * fixed light values and are overridden one by one for dark mode; tests_regressions.py fails
+ * when a CKEditor upgrade adds one that is neither overridden nor reviewed.
+ */
-/* Container styling to match Unfold form fields */
-.ck-editor-container {
- @apply relative w-full;
-}
-
-/* Main editor wrapper */
-.ck.ck-editor {
- @apply relative w-full bg-white dark:bg-base-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm;
- @apply focus-within:border-primary-600 focus-within:ring focus-within:ring-primary-500/40 dark:focus-within:border-primary-600 dark:focus-within:ring-primary-500/30;
- @apply transition-all duration-300 ease-in-out;
-}
-
-/* Toolbar styling */
-.ck.ck-toolbar {
- @apply bg-base-100 dark:bg-base-900 border-b border-gray-200 dark:border-gray-700;
- @apply px-2 py-1.5 rounded-t-lg;
-}
-
-/* Toolbar items container */
-.ck.ck-toolbar__items {
- @apply flex flex-wrap items-center gap-1;
-}
-
-/* Toolbar separators */
-.ck.ck-toolbar__separator {
- @apply w-px h-6 bg-gray-300 dark:bg-gray-600 mx-1;
-}
-
-/* Buttons styling refined */
-.ck.ck-button {
- @apply inline-flex items-center justify-center h-8 min-h-8 px-2.5 py-1.5;
- @apply text-[13px] font-medium text-gray-700 dark:text-gray-300 tracking-tight;
- @apply bg-transparent hover:bg-gray-100 dark:hover:bg-gray-700/70;
- @apply border border-transparent rounded-md;
- @apply transition-colors duration-150 ease-in-out;
- @apply cursor-pointer select-none;
- @apply focus:outline-none focus:ring-2 focus:ring-primary-500/40 dark:focus:ring-primary-500/30;
-}
-
-.ck.ck-button:hover {
- @apply bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100;
-}
-
-.ck.ck-button.ck-on {
- @apply bg-primary-100 dark:bg-primary-700/70 text-primary-700 dark:text-white;
- @apply border-primary-300 dark:border-primary-600;
-}
-
-.ck.ck-button.ck-disabled,
-.ck.ck-button:disabled {
- @apply opacity-50 cursor-not-allowed pointer-events-none;
-}
-
-/* Button icons */
-.ck.ck-button .ck-icon {
- @apply w-4 h-4 opacity-90 text-gray-600 dark:text-gray-300;
-}
-
-.ck.ck-button:hover .ck-icon,
-.ck.ck-button.ck-on .ck-icon {
- @apply opacity-100 text-primary-600 dark:text-white;
-}
-
-/* Dropdown buttons */
-.ck.ck-dropdown__button {
- @apply inline-flex items-center gap-2;
-}
-
-.ck.ck-dropdown__button .ck-dropdown__arrow {
- @apply w-3 h-3 opacity-60;
-}
-
-/* Split buttons styling */
-.ck.ck-splitbutton .ck-splitbutton__action {
- @apply rounded-l-md border-r border-gray-300 dark:border-gray-600;
-}
-
-.ck.ck-splitbutton .ck-splitbutton__arrow {
- @apply rounded-r-md px-2;
-}
-
-/* Dropdown panels */
-.ck.ck-dropdown__panel {
- @apply bg-white dark:bg-base-800 border border-gray-200 dark:border-gray-700;
- @apply rounded-lg shadow-lg mt-1 p-1 min-w-48;
-}
-
-/* High contrast nested dropdown variants (language, heading, font size) */
-.ck.ck-code-block-dropdown .ck-dropdown__panel,
-.ck.ck-font-size-dropdown .ck-dropdown__panel,
-.ck.ck-heading-dropdown .ck-dropdown__panel {
- @apply bg-white dark:bg-base-900 border border-gray-200 dark:border-gray-700 shadow-xl;
-}
-
-/* Improve list item readability inside panels */
-.ck.ck-dropdown__panel .ck.ck-list .ck-list__item { @apply rounded-md; }
-.ck.ck-dropdown__panel .ck.ck-list .ck-list__item + .ck-list__item { @apply mt-0.5; }
-.ck.ck-dropdown__panel .ck.ck-list .ck-list__item .ck-button { @apply w-full text-left px-3 py-2 text-[13px] font-medium; }
-.ck.ck-dropdown__panel .ck.ck-list .ck-list__item .ck-button:hover { @apply bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white; }
-.ck.ck-dropdown__panel .ck.ck-list .ck-list__item .ck-button.ck-on { @apply bg-primary-100 dark:bg-primary-700 text-primary-700 dark:text-white; }
-
-/* Visible check holder */
-.ck.ck-dropdown__panel .ck-list-item-button__check-holder { @apply w-4 h-4 mr-2 flex items-center justify-center text-[11px] rounded-sm border border-gray-300 dark:border-gray-600 bg-white dark:bg-base-800; }
-
-/* Ensure visibility when panel is shown */
-.ck.ck-dropdown__panel.ck-dropdown__panel-visible { @apply bg-white dark:bg-base-900; }
-
-/* Scrollbars inside dropdown lists */
-.ck.ck-dropdown__panel .ck.ck-list::-webkit-scrollbar { @apply w-2; }
-.ck.ck-dropdown__panel .ck.ck-list::-webkit-scrollbar-thumb { @apply bg-gray-300 dark:bg-gray-600 rounded; }
-
-/* Zebra table rows inside content */
-.ck.ck-content table tbody tr:nth-child(odd) { @apply bg-gray-50 dark:bg-base-800; }
-.ck.ck-content table tbody tr:nth-child(even) { @apply bg-white dark:bg-base-900; }
-.ck.ck-content table th, .ck.ck-content table td { @apply border border-gray-200 dark:border-gray-700; }
-
-/* Text selection highlight */
-.ck.ck-content ::selection { background: theme('colors.primary.500'); color: white; }
-.ck.ck-content ::-moz-selection { background: theme('colors.primary.500'); color: white; }
-
-/* Main editing area */
-.ck.ck-content {
- @apply min-h-48 max-h-[32rem] p-5 bg-white dark:bg-base-800;
- @apply text-gray-900 dark:text-gray-100 text-[15px] leading-relaxed;
- @apply rounded-b-lg border-none outline-none;
- @apply overflow-y-auto;
- font-family: 'Inter', system-ui, -apple-system, sans-serif;
-}
-
-.ck.ck-content:focus {
- @apply outline-none ring-0;
-}
-
-/* Content styling within editor */
-.ck.ck-content h1,
-.ck.ck-content h2,
-.ck.ck-content h3,
-.ck.ck-content h4,
-.ck.ck-content h5,
-.ck.ck-content h6 {
- @apply font-semibold text-gray-900 dark:text-gray-100 mb-3 mt-4;
-}
-
-.ck.ck-content h1 { @apply text-2xl; }
-.ck.ck-content h2 { @apply text-xl; }
-.ck.ck-content h3 { @apply text-lg; }
-.ck.ck-content h4 { @apply text-base font-bold; }
-.ck.ck-content h5 { @apply text-sm font-bold; }
-.ck.ck-content h6 { @apply text-xs font-bold uppercase tracking-wide; }
-
-.ck.ck-content p {
- @apply mb-4 text-gray-700 dark:text-gray-300;
-}
-
-.ck.ck-content blockquote {
- /* Refined blockquote styling */
- @apply relative pl-5 py-3 my-5 rounded-md overflow-hidden italic;
- @apply bg-gray-50 dark:bg-base-900 text-gray-700 dark:text-gray-300;
- @apply border-l-4 border-primary-500;
-}
-
-.ck.ck-content blockquote:before {
- content: "";
- @apply absolute left-0 top-0 h-full w-1 bg-primary-500;
-}
-
-.ck.ck-content ul,
-.ck.ck-content ol {
- @apply ml-6 mb-4;
-}
-
-.ck.ck-content ul {
- @apply list-disc;
-}
-
-.ck.ck-content ol {
- @apply list-decimal;
-}
-
-.ck.ck-content li {
- @apply mb-1 text-gray-700 dark:text-gray-300;
-}
-
-.ck.ck-content a {
- @apply text-primary-600 dark:text-primary-500 hover:text-primary-700 dark:hover:text-primary-400;
- @apply underline transition-colors duration-200;
-}
-
-.ck.ck-content code {
- @apply bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200;
- @apply px-2 py-1 rounded font-mono text-sm;
-}
-
-.ck.ck-content pre {
- @apply bg-gray-900/90 dark:bg-gray-900 text-gray-100 dark:text-gray-100;
- @apply border border-gray-800 dark:border-gray-700;
- @apply p-4 rounded-md overflow-x-auto my-5 font-mono text-[13px] leading-snug;
-}
-
-/* Table styling */
-.ck.ck-content table {
- @apply w-full border-collapse my-4;
-}
-
-.ck.ck-content table td,
-.ck.ck-content table th {
- @apply border border-gray-300 dark:border-gray-600 px-3 py-2;
-}
-
-.ck.ck-content table th {
- @apply bg-gray-100 dark:bg-gray-700 font-semibold text-gray-900 dark:text-gray-100;
-}
-
-.ck.ck-content table td {
- @apply bg-white dark:bg-base-800 text-gray-700 dark:text-gray-300;
-}
-
-/* Image widget styling */
-.ck.ck-content figure.image {
- @apply my-4 mx-auto max-w-full;
-}
-
-.ck.ck-content figure.image img {
- @apply max-w-full h-auto rounded-lg shadow-md;
-}
-
-/* Widget styling */
-.ck.ck-widget {
- @apply relative;
-}
-
-.ck.ck-widget.ck-widget_selected {
- @apply ring-2 ring-primary-400 ring-opacity-50 rounded;
-}
-
-.ck.ck-widget__type-around__button {
- @apply bg-primary-600 hover:bg-primary-700 text-white;
- @apply w-6 h-6 rounded-full flex items-center justify-center;
- @apply transition-colors duration-200;
-}
-
-/* Resizer styling */
-.ck.ck-widget__resizer {
- @apply border border-primary-400 bg-primary-50 dark:bg-primary-900 bg-opacity-20;
-}
-
-.ck.ck-widget__resizer__handle {
- @apply bg-primary-600 w-3 h-3 border border-primary-700 rounded;
-}
-
-/* Placeholder styling */
-.ck.ck-content .ck-placeholder:before {
- @apply text-gray-400 dark:text-gray-500 italic;
-}
-
-/* Balloon panels */
-.ck.ck-balloon-panel {
- @apply bg-white dark:bg-base-800 border border-gray-200 dark:border-gray-700;
- @apply rounded-lg shadow-lg p-2;
-}
-
-/* Link balloon styling */
-.ck.ck-link-form,
-.ck.ck-link-actions {
- @apply flex items-center gap-2 p-2;
-}
-
-.ck.ck-link-form .ck-input {
- @apply flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600;
- @apply bg-white dark:bg-base-800 text-gray-900 dark:text-gray-100;
- @apply rounded-md text-sm focus:ring-2 focus:ring-primary-300 focus:border-primary-600;
-}
-
-/* Color picker styling */
-.ck.ck-color-selector {
- @apply grid grid-cols-6 gap-1 p-2;
-}
-
-.ck.ck-color-selector .ck-color-selector__option {
- @apply w-7 h-7 rounded-md border border-gray-300 dark:border-gray-600 shadow-sm;
- @apply hover:scale-110 transition-transform duration-150;
-}
-.ck.ck-color-selector .ck-color-selector__option.ck-on {
- @apply ring-2 ring-primary-500 ring-offset-1 ring-offset-white dark:ring-offset-base-800 border-primary-500;
-}
-
-/* Font size dropdown */
-.ck.ck-font-size-dropdown .ck-dropdown__panel {
- @apply max-h-48 overflow-y-auto;
-}
-
-.ck.ck-list .ck-list__item {
- @apply px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700;
- @apply text-gray-700 dark:text-gray-300 cursor-pointer;
- @apply transition-colors duration-200;
-}
-
-.ck.ck-list .ck-list__item.ck-on {
- @apply bg-primary-100 dark:bg-primary-700 text-primary-700 dark:text-white font-medium rounded;
-}
-
-/* Code block dropdown */
-.ck.ck-code-block-dropdown .ck-dropdown__panel {
- @apply min-w-56;
-}
-
-/* Media embed styling */
-.ck.ck-content .ck-media__wrapper {
- @apply my-4 rounded-lg overflow-hidden shadow-md;
-}
-
-.ck.ck-content .ck-media__placeholder {
- @apply bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400;
- @apply p-8 text-center rounded-lg border-2 border-dashed border-gray-300 dark:border-gray-600;
-}
-
-/* Todo list styling */
-.ck.ck-content .todo-list {
- @apply list-none ml-0;
-}
-
-.ck.ck-content .todo-list li {
- @apply flex items-start gap-3 mb-2;
-}
-
-.ck.ck-content .todo-list li .todo-list__label {
- @apply flex items-center gap-2;
-}
-
-.ck.ck-content .todo-list li .todo-list__label input {
- @apply w-4 h-4 text-primary-600 border border-gray-300 dark:border-gray-600 rounded;
- @apply focus:ring-2 focus:ring-primary-300;
-}
-
-/* Highlight styling */
-.ck.ck-content mark {
- @apply bg-yellow-200 dark:bg-yellow-600 text-gray-900 dark:text-gray-100;
- @apply px-1 rounded;
-}
-
-/* Source editing mode */
-.ck.ck-source-editing-area {
- @apply w-full min-h-48 p-4 bg-gray-50 dark:bg-gray-900;
- @apply text-gray-900 dark:text-gray-100 font-mono text-sm;
- @apply border border-gray-300 dark:border-gray-600 rounded-lg;
- @apply focus:ring-2 focus:ring-primary-300 focus:border-primary-600;
- @apply resize-y;
-}
-
-/* Word count styling */
-.word-count {
- @apply mt-2 text-right;
-}
-
-.ck.ck-word-count {
- @apply text-xs text-gray-500 dark:text-gray-400 flex gap-4 justify-end;
-}
-
-/* Responsive adjustments */
-@media (max-width: 768px) {
- .ck.ck-toolbar__items {
- @apply gap-0.5;
- }
-
- .ck.ck-button {
- @apply h-7 min-h-7 px-2 text-xs;
- }
-
- .ck.ck-content {
- @apply min-h-32 p-3 text-sm;
- }
-
- .ck.ck-dropdown__panel {
- @apply min-w-full;
- }
-}
-
-/* Animation improvements */
-@media (prefers-reduced-motion: no-preference) {
- .ck.ck-button {
- @apply transition-all duration-200 ease-in-out;
- }
-
- .ck.ck-dropdown__panel {
- @apply transition-opacity duration-200 ease-in-out;
- }
-
- .ck.ck-widget {
- @apply transition-all duration-200 ease-in-out;
- }
-}
-
-/* Dark mode specific adjustments */
-@media (prefers-color-scheme: dark) {
- .ck.ck-editor {
- @apply border-gray-600;
- }
-
- .ck.ck-content {
- @apply scrollbar-thin scrollbar-track-gray-800 scrollbar-thumb-gray-600;
- }
-}
-
-/* Focus states for accessibility */
-.ck.ck-button:focus-visible {
- @apply ring-2 ring-primary-400 ring-offset-1 ring-offset-white dark:ring-offset-base-800;
-}
-
-.ck.ck-content:focus {
- @apply ring-0 outline-none;
-}
-
-/* High contrast mode support */
-@media (prefers-contrast: high) {
- .ck.ck-button {
- @apply border border-gray-400 dark:border-gray-500;
- }
-
- .ck.ck-content {
- @apply border border-gray-400 dark:border-gray-500;
- }
-}
-
-/* Override default CKEditor colors for better integration */
:root {
- --ck-color-base-background: theme('colors.white');
- --ck-color-base-foreground: theme('colors.gray.100');
- --ck-color-focus-border: theme('colors.primary.600');
- --ck-color-text: theme('colors.gray.900');
- --ck-color-shadow-drop: rgba(0, 0, 0, 0.15);
- --ck-color-shadow-inner: rgba(0, 0, 0, 0.1);
- --ck-color-button-default-background: transparent;
- --ck-color-button-default-hover-background: theme('colors.gray.100');
- --ck-color-button-on-background: theme('colors.primary.100');
- --ck-color-button-on-hover-background: theme('colors.primary.200');
- --ck-color-dropdown-panel-background: theme('colors.white');
- --ck-color-dropdown-panel-border: theme('colors.gray.200');
- --ck-color-dialog-background: theme('colors.white');
- --ck-color-dialog-form-header-border: theme('colors.gray.200');
- --ck-color-input-background: theme('colors.white');
- --ck-color-input-border: theme('colors.gray.300');
- --ck-color-input-error-border: theme('colors.red.500');
- --ck-color-list-background: theme('colors.white');
- --ck-color-list-button-hover-background: theme('colors.gray.100');
- --ck-color-list-button-on-background: theme('colors.primary.100');
- --ck-color-panel-background: theme('colors.white');
- --ck-color-panel-border: theme('colors.gray.200');
- --ck-color-toolbar-background: theme('colors.gray.50');
- --ck-color-toolbar-border: theme('colors.gray.200');
- --ck-color-tooltip-background: theme('colors.gray.800');
- --ck-color-tooltip-text: theme('colors.white');
- --ck-color-engine-placeholder-text: theme('colors.gray.400');
- --ck-color-upload-bar-background: theme('colors.primary.600');
- --ck-color-link-default: theme('colors.primary.600');
- --ck-color-link-selected-background: theme('colors.primary.100');
-}
-
-[data-theme="dark"] {
- --ck-color-base-background: theme('colors.base.800');
- --ck-color-base-foreground: theme('colors.base.700');
- --ck-color-focus-border: theme('colors.primary.600');
- --ck-color-text: theme('colors.gray.100');
- --ck-color-shadow-drop: rgba(0, 0, 0, 0.3);
- --ck-color-shadow-inner: rgba(0, 0, 0, 0.2);
- --ck-color-button-default-background: transparent;
- --ck-color-button-default-hover-background: theme('colors.gray.700');
- --ck-color-button-on-background: theme('colors.primary.600');
- --ck-color-button-on-hover-background: theme('colors.primary.700');
- --ck-color-dropdown-panel-background: theme('colors.base.800');
- --ck-color-dropdown-panel-border: theme('colors.gray.600');
- --ck-color-dialog-background: theme('colors.base.800');
- --ck-color-dialog-form-header-border: theme('colors.gray.600');
- --ck-color-input-background: theme('colors.base.800');
- --ck-color-input-border: theme('colors.gray.600');
- --ck-color-input-error-border: theme('colors.red.500');
- --ck-color-list-background: theme('colors.base.800');
- --ck-color-list-button-hover-background: theme('colors.gray.700');
- --ck-color-list-button-on-background: theme('colors.primary.600');
- --ck-color-panel-background: theme('colors.base.800');
- --ck-color-panel-border: theme('colors.gray.600');
- --ck-color-toolbar-background: theme('colors.base.900');
- --ck-color-toolbar-border: theme('colors.gray.700');
- --ck-color-tooltip-background: theme('colors.gray.700');
- --ck-color-tooltip-text: theme('colors.white');
- --ck-color-engine-placeholder-text: theme('colors.gray.500');
- --ck-color-upload-bar-background: theme('colors.primary.600');
- --ck-color-link-default: theme('colors.primary.500');
- --ck-color-link-selected-background: theme('colors.primary.800');
-}
-
-/* Animation keyframes */
-@keyframes ck-fade-in {
- from {
- opacity: 0;
- transform: translateY(-4px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* Apply fade-in animation to dropdowns */
-.ck.ck-dropdown__panel {
- animation: ck-fade-in 0.2s ease-out;
-}
-
-/* Custom scrollbar for content area */
-.ck.ck-content::-webkit-scrollbar {
- @apply w-2;
-}
-
-.ck.ck-content::-webkit-scrollbar-track {
- @apply bg-gray-100 dark:bg-gray-800 rounded;
-}
-
-.ck.ck-content::-webkit-scrollbar-thumb {
- @apply bg-gray-400 dark:bg-gray-600 rounded hover:bg-gray-500 dark:hover:bg-gray-500;
-}
-
-/* Ensure proper spacing for nested editor containers */
-.ck-editor-container .ck-editor-container {
- @apply mt-0;
-}
-
-/* Integration with Django form styling */
-.form-row .ck-editor-container,
-.field-box .ck-editor-container {
- @apply w-full;
-}
-
-/* Error state styling */
-.errors .ck.ck-editor {
- @apply border-red-600 dark:border-red-500;
-}
-
-.errors .ck.ck-editor:focus-within {
- @apply ring-red-200 dark:ring-red-600/40 border-red-600 dark:border-red-500;
-}
-
-/* Success state styling */
-.success .ck.ck-editor {
- @apply border-green-600 dark:border-green-500;
-}
-
-.success .ck.ck-editor:focus-within {
- @apply ring-green-200 dark:ring-green-600/40 border-green-600 dark:border-green-500;
+ /* unfold's font, written out: Tailwind scans this file, and referencing its font theme variable
+ makes styles.css emit Tailwind's default font, overriding unfold's */
+ --ck-content-font-family: "Inter", system-ui, -apple-system, sans-serif;
+
+ --ck-color-base-border: var(--color-base-200, #ccced1);
+ --ck-color-base-text: var(--color-font-important-light, #333);
+ --ck-color-focus-border: var(--color-primary-600, hsl(218, 81.8%, 56.9%));
+ --ck-color-focus-outer-shadow: var(--color-primary-200, #cae1fc);
+ --ck-color-link-default: var(--color-primary-600, #0000f0);
+
+ --ck-color-button-on-color: var(--color-primary-600, #2977ff);
+ --ck-color-button-on-background: var(--color-primary-50, #f0f7ff);
+ --ck-color-button-on-hover-background: var(--color-primary-100, #dbecff);
+ --ck-color-button-on-active-background: var(--color-primary-100, #dbecff);
+}
+
+/* unfold's dark mode: the "dark" class on , also set for daisyUI dark themes */
+html.dark {
+ --wn-ck-primary: var(--color-primary-500, #0ea5e9);
+
+ --ck-color-base-background: var(--color-base-900, #111827);
+ --ck-color-base-foreground: var(--color-base-800, #1f2937);
+ --ck-color-base-border: var(--color-base-700, #374151);
+ --ck-color-base-text: var(--color-font-important-dark, #f9fafb);
+ --ck-color-focus-border: var(--wn-ck-primary);
+ --ck-color-focus-outer-shadow: color-mix(in srgb, var(--wn-ck-primary) 35%, transparent);
+ --ck-color-shadow-drop: rgba(0, 0, 0, 0.4);
+ --ck-color-shadow-drop-active: rgba(0, 0, 0, 0.5);
+ --ck-color-shadow-inner: rgba(0, 0, 0, 0.3);
+
+ /* buttons */
+ --ck-color-button-default-hover-background: var(--color-base-800, #1f2937);
+ --ck-color-button-default-active-background: var(--color-base-700, #374151);
+ --ck-color-button-on-color: var(--color-primary-400, #38bdf8);
+ --ck-color-button-on-background: color-mix(in srgb, var(--wn-ck-primary) 20%, transparent);
+ --ck-color-button-on-hover-background: color-mix(in srgb, var(--wn-ck-primary) 30%, transparent);
+ --ck-color-button-on-active-background: color-mix(in srgb, var(--wn-ck-primary) 35%, transparent);
+ --ck-color-button-on-disabled-background: var(--color-base-800, #1f2937);
+ --ck-color-button-action-text: #fff;
+ --ck-color-split-button-hover-background: var(--color-base-700, #374151);
+ --ck-color-split-button-hover-border: var(--color-base-600, #4b5563);
+
+ /* inputs and placeholders */
+ --ck-color-input-disabled-background: var(--color-base-800, #1f2937);
+ --ck-color-input-disabled-text: var(--color-font-subtle-dark, #9ca3af);
+ --ck-color-engine-placeholder-text: var(--color-font-subtle-dark, #9ca3af);
+ --ck-color-media-embed-placeholder-url-text: var(--color-font-subtle-dark, #9ca3af);
+
+ /* selection, widgets, links */
+ --ck-color-editable-blur-selection: var(--color-base-700, #374151);
+ --ck-color-widget-blurred-border: var(--color-base-600, #4b5563);
+ --ck-color-link-default: var(--color-primary-400, #38bdf8);
+ --ck-color-link-selected-background: color-mix(in srgb, var(--wn-ck-primary) 20%, transparent);
+ --ck-color-code-block-label-background: var(--color-base-600, #4b5563);
+ --ck-color-image-caption-highlighted-background: var(--color-base-600, #4b5563);
+ --ck-color-table-caption-highlighted-background: var(--color-base-600, #4b5563);
+
+ /* panels: keyboard shortcuts dialog (Alt+0), style grid */
+ --ck-accessibility-help-dialog-border-color: var(--color-base-700, #374151);
+ --ck-accessibility-help-dialog-code-background-color: var(--color-base-700, #374151);
+ --ck-accessibility-help-dialog-kbd-shadow-color: var(--color-base-950, #030712);
+ --ck-style-panel-button-label-background: var(--color-base-800, #1f2937);
+ --ck-style-panel-button-hover-label-background: var(--color-base-700, #374151);
+ --ck-style-panel-button-hover-border-color: var(--color-base-600, #4b5563);
+
+ /* editing area: CKEditor's content text is #000 by default */
+ --ck-content-font-color: var(--color-font-important-dark, #f9fafb);
+ --ck-content-color-image-caption-background: var(--color-base-800, #1f2937);
+ --ck-content-color-image-caption-text: var(--color-font-default-dark, #d1d5db);
+ --ck-content-color-table-caption-background: var(--color-base-800, #1f2937);
+ --ck-content-color-table-caption-text: var(--color-font-default-dark, #d1d5db);
+ --ck-content-color-mention-text: #f472b6;
+ --ck-table-content-default-border-color: var(--color-base-600, #4b5563);
+ --ck-table-layout-default-border-color: var(--color-base-600, #4b5563);
+ --ck-table-border-none-helper-line-color: var(--color-base-600, #4b5563);
+}
+
+/* native scrollbars and controls inside the editor; the page's color-scheme is unfold's */
+html.dark .ck {
+ color-scheme: dark;
+}
+
+/* fixed content colors without a variable */
+
+/* code blocks: #353535 text */
+html.dark .ck-content pre {
+ color: var(--color-font-default-dark, #d1d5db);
+ border-color: var(--color-base-700, #374151);
+}
+
+/* todo list checkboxes: #333 border (checked ones keep CKEditor's green) */
+html.dark .ck-content .todo-list .todo-list__label > input::before,
+html.dark .ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input::before {
+ border-color: var(--color-base-400, #9ca3af);
+}
+
+/* find & replace matches: --ck-color-text (light in dark mode) on yellow / #ff9633 */
+html.dark .ck-find-result {
+ color: var(--color-base-900, #111827);
}