Summary
Every time a site's cache is fully cleared, the Color module regenerates a brand new random directory name for a theme's derived CSS files — even when the color palette hasn't changed at all. This gets written into the theme's config ([theme].settings), so a plain cache clear produces a config change with no real content difference.
What happens without a fix (failure mode)
Take any theme with Color module support and a custom color scheme set (for example Basis or Bartik). Every time all caches are cleared — by running drush cache-clear all, bee cc all, clicking "Clear all caches" in the admin UI, or any code path that calls a full cache flush (including some automated ones, like after a config import) — Backdrop rewrites that theme's settings config with new, different-looking file paths such as:
"public://color/basis-726132a6/base.css"
becomes
"public://color/basis-faea8157/base.css"
The actual CSS content is identical; only the random directory name changed. If a site uses Backdrop's Configuration Management to track config in git, this shows up as an unwanted diff in config/active/*.settings.json after every single cache clear, on every environment including production. In practice this means:
- Config exports/diffs are never "clean" on a color-enabled site — something always looks changed even when nothing was.
- It's easy to mistake this noise for a real, meaningful configuration change during a deploy or code review.
- Old generated CSS directories under
public://color/ are deleted and re-created every time, for no reason.
Root cause
In color_save_configuration() (core/modules/color/color.module):
$id = $theme_name . '-' . substr(hash('sha256', serialize($palette) . microtime()), 0, 8);
Because microtime() is included in the hash, $id is different on every call, even for the exact same $palette. This function is called from color_rebuild_settings(), which is invoked by color_flush_caches() — Color's implementation of hook_flush_caches() — so it runs on every full cache clear, not just when a color scheme is actually changed.
Steps to reproduce
- Enable a theme with Color module support (e.g. Basis) and set/save a custom color palette on its theme settings page.
- Note the
color.stylesheets / color.files paths in [theme].settings config.
- Run a full cache clear (
drush cache-clear all or equivalent).
- Observe the paths changed to a new random directory, even though the palette is unchanged.
- Repeat step 3 — it changes again every time.
Proposed fix
A pull request is attached that makes the $id deterministic (based only on the theme name and palette, dropping microtime()), so an unchanged palette reuses the same directory and no longer produces a config diff. Real palette or theme CSS changes still get a new directory as before. Browser cache-busting after a real cache clear is unaffected, since Backdrop already appends a separate, global cache-busting query string to all CSS/JS requests on every cache clear (_backdrop_flush_css_js()), independent of this file path.
Summary
Every time a site's cache is fully cleared, the Color module regenerates a brand new random directory name for a theme's derived CSS files — even when the color palette hasn't changed at all. This gets written into the theme's config (
[theme].settings), so a plain cache clear produces a config change with no real content difference.What happens without a fix (failure mode)
Take any theme with Color module support and a custom color scheme set (for example Basis or Bartik). Every time all caches are cleared — by running
drush cache-clear all,bee cc all, clicking "Clear all caches" in the admin UI, or any code path that calls a full cache flush (including some automated ones, like after a config import) — Backdrop rewrites that theme's settings config with new, different-looking file paths such as:becomes
The actual CSS content is identical; only the random directory name changed. If a site uses Backdrop's Configuration Management to track config in git, this shows up as an unwanted diff in
config/active/*.settings.jsonafter every single cache clear, on every environment including production. In practice this means:public://color/are deleted and re-created every time, for no reason.Root cause
In
color_save_configuration()(core/modules/color/color.module):Because
microtime()is included in the hash,$idis different on every call, even for the exact same$palette. This function is called fromcolor_rebuild_settings(), which is invoked bycolor_flush_caches()— Color's implementation ofhook_flush_caches()— so it runs on every full cache clear, not just when a color scheme is actually changed.Steps to reproduce
color.stylesheets/color.filespaths in[theme].settingsconfig.drush cache-clear allor equivalent).Proposed fix
A pull request is attached that makes the
$iddeterministic (based only on the theme name and palette, droppingmicrotime()), so an unchanged palette reuses the same directory and no longer produces a config diff. Real palette or theme CSS changes still get a new directory as before. Browser cache-busting after a real cache clear is unaffected, since Backdrop already appends a separate, global cache-busting query string to all CSS/JS requests on every cache clear (_backdrop_flush_css_js()), independent of this file path.