Try โ€‚โ€‰HackMD

Tech Brief django CMS dark mode

tags: workgroup

Since Django 3.2 the built-in admin app comes with built-in support of dark mode css styles. Many developer use it - also when evaluating software. django CMS uses the admin for front-end editing, page admin and so forth.

Workgroup Members

Add names here

Workroup Leader

Fabian

Workgroup Goals

  • Make django CMS compatible with dark-mode of Django
  • Support dark mode with djangocms-admin-style
  • Develop guidlines on how django cms apps and third party apps should use color styling so that the whole system consistently displays light and dark content
  • Check that all solutions are independent of djangocms-admin-style and also work with plain Django.
  • Have fun with dark mode!

Milestones

โœ… Done, PR merged and released.

โ˜‘๏ธ Done, PR created and reviewed.

โš™๏ธ Work in progress

status date description
โœ… 27.03.2022 Dark mode for djangocms-admin-style
โœ… 04.04.2022 Dark mode for djangocms-text-ckeditor
โ˜‘๏ธ 12.03.2022 Dark mode for django CMS v3
โš™๏ธ xx.xx.xxxx Guidelines for admin styles in CMS apps
โš™๏ธ xx.xx.xxxx Dark mode for djangocms-alias
โš™๏ธ xx.xx.xxxx Dark mode for djangocms-versioning
โš™๏ธ xx.xx.xxxx Dark mode for django CMS v4

Tentative Roadmap

  • Q1 - Implement dark mode for django CMS v3 core and djangocms-admin-style
  • Q2 - Develop and decide on guidelines
  • Q3 - Adapt core apps for dark mode
  • Q3 - Improvement of colors

Approximate specification

Main points:

  • Replace fixed color values in css by css variables
  • Replace scss functions like lighten, darken, and rgba which do not work with css variables by css filters (e.g., brightness())
  • Define CMS color variables on existing django CMS styles for both django CMS core and djangocms-admin-style
  • Use CMS color variables with (the smaller set of) django variables as fall back (in case djangocms-admin-style is not installed).

CMS colors

CMS color list

The django CMS core styling uses a set of grayscale colors. See cms/static/cms/sass/settings/_cms.scss

These are the colors designed for light mode:

Name Color Variable name
$white #ffffff --dca-white
$black #000000 --dca-black
$color-primary #0bf -
$color-danger #669933 -
$color-warning #ff0000 -
$gray #666 --dca-gray
$gray-lightest #f2f2f2 --dca-gray-lightest
$gray-lighter #ddd --dca-gray-lighter
$gray-light #999 --dca-gray-light
$gray-darker #454545 --dca-gray-darker
$gray-darkest #333 --dca-gray-darkest
$gray-super-lightest #f7f7f7 --dca-gray-super-lightest

The dca- prefix is used to avoid conflicts with other css variables and stands for "django CMS association".

Fallback colors

The admin frontend pulls the style from django admin styles and - if present - from djangocms-admin-style. Django itself also uses css variables to implement admin mode, these can be used as dark mode-aware fall-back colors.

Variable name Color Fallback Color
--dca-white #ffffff --body-bg #ffffff
--dca-black #000000 --body-fg #303030
--dca-gray #666 --body-quiet-color #666
--dca-gray-lightest #f2f2f2 --darkened-bg #f8f8f8
--dca-gray-lighter #ddd --border-color #ccc
--dca-gray-light #999 --close-button-bg #888
--dca-gray-darker #454545
--dca-gray-darkest #333
--dca-gray-super-lightest #f7f7f7
--dca-primary #00bbff --primary #79aec8

If at all posible only the first four fallbacks should be used. Other django admin css variables might be overwritten by a theme and might not represent a level of gray.

Mode selection

Three modes may be selected:

auto
The displayed color scheme is set by the browser/OS. This is the default behavior.
light
Light mode is enforced (e.g., to support plugins that are not compatible with dark mode). Add data-color-scheme="light" to the document's html tag, i.e., <html data-color-scheme="light">.
dark
Dark mode is enforced. Add data-color-scheme="dark" to the document's html tag.

Recommendation draft for cms apps (django cms association and third party)

  • Try avoid using color, background etc. styles where possible and meaningful
  • If necessary use as few as possible standard django CMS colors (preferably from see above list with fallback colors)
  • Usage: var(--dca-color-var, var(--fallback-color-var, #xxxxxx)) where #xxxxxx represents the light version of the color.
  • Avoid media queries like @media (prefers-color-scheme: dark) since they would ignore forced settings to light or dark.

Edge case 3rd party libraries

Third party libraries might present an edge case: CKEditor 4, for example, does not offer dark mode compatible skins for djangocms-text-ckeditor. A similar situation exists for the code editor Ace which is used by djangocms-frontend or djangocms-snippet. Two options exist:

  1. For CKEditor 4 we patch the standard skin during the build process of the editor. The patch replaces color literals by var(--dca-color-var, var(--fallback-color-var), #color) in the already minified css. See this python scrippt.

  2. For ACE dark color designs exist. When invoking the editor in javascript we check for the preferred mode (with the drawback that after invoking the editor a change in preference by the user is only reflected after a reload of the page):

    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ // init editor with settings
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ var editor = ace.edit(div[0]);
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ var darkMode = False;
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ 
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ if (window.parent.CMS.API.Toolbar.get_color_scheme) {
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹     darkMode = window.parent.CMS.API.Toolbar.get_color_scheme() === 'dark';
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ } else {
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹     // django CMS pre-3.11
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹     darkMode =  window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ }
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ if (darkMode) {
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹     editor.setTheme('ace/theme/tomorrow_night');
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ }  else {
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹     editor.setTheme('ace/theme/github');
    โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ }
    

    Of course, an improved implementation will not inline the dark and light themes as literals but revert to settings to allow for better configurability.