DEV: Backend support for light/dark mode in color palettes (#30893)

We're embarking on a project for overhauling the color palette and theme
systems in Discourse. As part of this project, we're making each color
palette include light and dark modes instead of the status quo of
requiring 2 separate color palettes to implement light and dark modes.

This commit is a first step towards that goal; it adds a code path for
generating and serving `color_definitions` stylesheets using the
built-in dark variant of a color palette. All of this code path is
behind a default-off site setting `use_overhauled_theme_color_palette`,
so there's no change in behavior unless the setting is enabled.

Internal topic: t/141467.
This commit is contained in:
Osama Sayegh
2025-01-23 15:54:49 +03:00
committed by GitHub
parent 13f86c99ea
commit 10f34ddf86
10 changed files with 193 additions and 41 deletions

View File

@ -145,19 +145,21 @@ module Stylesheet
if @color_scheme_id
colors =
begin
ColorScheme.find(@color_scheme_id).resolved_colors
ColorScheme.find(@color_scheme_id).resolved_colors(dark: @dark)
rescue StandardError
ColorScheme.base_colors
end
elsif (@theme_id && !theme.component)
colors = theme&.color_scheme&.resolved_colors || ColorScheme.base_colors
colors = theme&.color_scheme&.resolved_colors(dark: @dark) || ColorScheme.base_colors
else
# this is a slightly ugly backwards compatibility fix,
# we shouldn't be using the default theme color scheme for components
# (most components use CSS custom properties which work fine without this)
colors =
Theme.find_by_id(SiteSetting.default_theme_id)&.color_scheme&.resolved_colors ||
ColorScheme.base_colors
Theme
.find_by_id(SiteSetting.default_theme_id)
&.color_scheme
&.resolved_colors(dark: @dark) || ColorScheme.base_colors
end
colors.each { |n, hex| contents << "$#{n}: ##{hex} !default; " }
@ -178,6 +180,7 @@ module Stylesheet
@theme = options[:theme]
@theme_id = options[:theme_id]
@color_scheme_id = options[:color_scheme_id]
@dark = options[:dark]
if @theme && !@theme_id
# make up an id so other stuff does not bail out

View File

@ -42,10 +42,11 @@ class Stylesheet::Manager
cache.clear_regex(/#{plugin}/)
end
def self.color_scheme_cache_key(color_scheme, theme_id = nil)
def self.color_scheme_cache_key(color_scheme, theme_id = nil, dark: false)
color_scheme_name = Slug.for(color_scheme.name) + color_scheme&.id.to_s
theme_string = theme_id ? "_theme#{theme_id}" : ""
"#{COLOR_SCHEME_STYLESHEET}_#{color_scheme_name}_#{theme_string}_#{Discourse.current_hostname}"
dark_string = dark ? "_dark" : ""
"#{COLOR_SCHEME_STYLESHEET}_#{color_scheme_name}_#{theme_string}_#{Discourse.current_hostname}#{dark_string}"
end
def self.precompile_css
@ -114,14 +115,17 @@ class Stylesheet::Manager
theme = manager.get_theme(theme_id)
[theme_color_scheme, *color_schemes].compact.uniq.each do |scheme|
$stderr.puts "precompile target: #{COLOR_SCHEME_STYLESHEET} #{theme.name} (#{scheme.name})"
Stylesheet::Manager::Builder.new(
target: COLOR_SCHEME_STYLESHEET,
theme: theme,
color_scheme: scheme,
manager: manager,
).compile(force: true)
[true, false].each do |dark|
mode = dark ? "dark" : "light"
$stderr.puts "precompile target: #{COLOR_SCHEME_STYLESHEET} #{theme.name} (#{scheme.name}) (#{mode})"
Stylesheet::Manager::Builder.new(
target: COLOR_SCHEME_STYLESHEET,
theme: theme,
color_scheme: scheme,
manager: manager,
dark:,
).compile(force: true)
end
end
clear_color_scheme_cache!
@ -336,7 +340,7 @@ class Stylesheet::Manager
end
end
def color_scheme_stylesheet_details(color_scheme_id = nil, media)
def color_scheme_stylesheet_details(color_scheme_id = nil, media, dark: false)
theme_id = @theme_id || SiteSetting.default_theme_id
color_scheme =
@ -353,10 +357,10 @@ class Stylesheet::Manager
target = COLOR_SCHEME_STYLESHEET.to_sym
current_hostname = Discourse.current_hostname
cache_key = self.class.color_scheme_cache_key(color_scheme, theme_id)
cache_key = self.class.color_scheme_cache_key(color_scheme, theme_id, dark:)
cache.defer_get_set(cache_key) do
stylesheet = { color_scheme_id: color_scheme.id }
stylesheet = { color_scheme_id: color_scheme.id, dark: }
theme = get_theme(theme_id)
@ -366,6 +370,7 @@ class Stylesheet::Manager
theme: get_theme(theme_id),
color_scheme: color_scheme,
manager: self,
dark:,
)
builder.compile unless File.exist?(builder.stylesheet_fullpath)
@ -376,8 +381,8 @@ class Stylesheet::Manager
end
end
def color_scheme_stylesheet_preload_tag(color_scheme_id = nil, media = "all")
stylesheet = color_scheme_stylesheet_details(color_scheme_id, media)
def color_scheme_stylesheet_preload_tag(color_scheme_id = nil, media = "all", dark: false)
stylesheet = color_scheme_stylesheet_details(color_scheme_id, media, dark:)
return "" if !stylesheet
@ -386,8 +391,13 @@ class Stylesheet::Manager
%[<link href="#{href}" rel="preload" as="style"/>].html_safe
end
def color_scheme_stylesheet_link_tag(color_scheme_id = nil, media = "all", preload_callback = nil)
stylesheet = color_scheme_stylesheet_details(color_scheme_id, media)
def color_scheme_stylesheet_link_tag(
color_scheme_id = nil,
media = "all",
preload_callback = nil,
dark: false
)
stylesheet = color_scheme_stylesheet_details(color_scheme_id, media, dark:)
return "" if !stylesheet

View File

@ -3,11 +3,12 @@
class Stylesheet::Manager::Builder
attr_reader :theme
def initialize(target: :desktop, theme: nil, color_scheme: nil, manager:)
def initialize(target: :desktop, theme: nil, color_scheme: nil, manager:, dark: false)
@target = target
@theme = theme
@color_scheme = color_scheme
@manager = manager
@dark = dark
end
def compile(opts = {})
@ -46,6 +47,7 @@ class Stylesheet::Manager::Builder
source_map_file: source_map_url_relative_from_stylesheet,
color_scheme_id: @color_scheme&.id,
load_paths: load_paths,
dark: @dark,
)
rescue SassC::SyntaxError, SassC::NotRenderedError => e
if Stylesheet::Importer::THEME_TARGETS.include?(@target.to_s)
@ -119,13 +121,14 @@ class Stylesheet::Manager::Builder
end
def qualified_target
dark_string = @dark ? "_dark" : ""
if is_theme?
"#{@target}_#{theme&.id}"
elsif @color_scheme
"#{@target}_#{scheme_slug}_#{@color_scheme&.id}_#{@theme&.id}"
"#{@target}_#{scheme_slug}_#{@color_scheme&.id}_#{@theme&.id}#{dark_string}"
else
scheme_string = theme&.color_scheme ? "_#{theme.color_scheme.id}" : ""
"#{@target}#{scheme_string}"
"#{@target}#{scheme_string}#{dark_string}"
end
end
@ -245,8 +248,9 @@ class Stylesheet::Manager::Builder
digest_string = "#{current_hostname}-"
if cs
theme_color_defs = resolve_baked_field(:common, :color_definitions)
dark_string = @dark ? "-dark" : ""
digest_string +=
"#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.fs_asset_cachebuster}-#{fonts}"
"#{RailsMultisite::ConnectionManagement.current_db}-#{cs&.id}-#{cs&.version}-#{theme_color_defs}-#{Stylesheet::Manager.fs_asset_cachebuster}-#{fonts}#{dark_string}"
else
digest_string += "defaults-#{Stylesheet::Manager.fs_asset_cachebuster}-#{fonts}"