mirror of
https://github.com/discourse/discourse.git
synced 2025-02-06 11:28:18 +00:00
882b0aac19
Themes can now declare custom colors that get compiled in core's color definitions stylesheet, thus allowing themes to better support dark/light color schemes. For example, if you need your theme to use tertiary for an element in a light color scheme and quaternary in a dark scheme, you can add the following SCSS to your theme's `color_definitions.scss` file: ``` :root { --mytheme-tertiary-or-quaternary: #{dark-light-choose($tertiary, $quaternary)}; } ``` And then use the `--mytheme-tertiary-or-quaternary` variable as the color property of that element. You can also use this file to add color variables that use SCSS color transformation functions (lighten, darken, saturate, etc.) without compromising your theme's compatibility with different color schemes.
314 lines
9.0 KiB
Ruby
314 lines
9.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ColorScheme < ActiveRecord::Base
|
|
|
|
# rubocop:disable Layout/HashAlignment
|
|
|
|
CUSTOM_SCHEMES = {
|
|
'Dark': {
|
|
"primary" => 'dddddd',
|
|
"secondary" => '222222',
|
|
"tertiary" => '0f82af',
|
|
"quaternary" => 'c14924',
|
|
"header_background" => '111111',
|
|
"header_primary" => 'dddddd',
|
|
"highlight" => 'a87137',
|
|
"danger" => 'e45735',
|
|
"success" => '1ca551',
|
|
"love" => 'fa6c8d'
|
|
},
|
|
# By @itsbhanusharma
|
|
'Neutral': {
|
|
"primary" => '000000',
|
|
"secondary" => 'ffffff',
|
|
"tertiary" => '51839b',
|
|
"quaternary" => 'b85e48',
|
|
"header_background" => '333333',
|
|
"header_primary" => 'f3f3f3',
|
|
"highlight" => 'ecec70',
|
|
"danger" => 'b85e48',
|
|
"success" => '518751',
|
|
"love" => 'fa6c8d'
|
|
},
|
|
# By @Flower_Child
|
|
'Grey Amber': {
|
|
"primary" => 'd9d9d9',
|
|
"secondary" => '3d4147',
|
|
"tertiary" => 'fdd459',
|
|
"quaternary" => 'fdd459',
|
|
"header_background" => '36393e',
|
|
"header_primary" => 'd9d9d9',
|
|
"highlight" => 'fdd459',
|
|
"danger" => 'e45735',
|
|
"success" => 'fdd459',
|
|
"love" => 'fdd459'
|
|
},
|
|
# By @rafafotes
|
|
'Shades of Blue': {
|
|
"primary" => '203243',
|
|
"secondary" => 'eef4f7',
|
|
"tertiary" => '416376',
|
|
"quaternary" => '5e99b9',
|
|
"header_background" => '86bddb',
|
|
"header_primary" => 'ffffff',
|
|
"highlight" => '86bddb',
|
|
"danger" => 'bf3c3c',
|
|
"success" => '70db82',
|
|
"love" => 'fc94cb'
|
|
},
|
|
# By @mikechristopher
|
|
'Latte': {
|
|
"primary" => 'f2e5d7',
|
|
"secondary" => '262322',
|
|
"tertiary" => 'f7f2ed',
|
|
"quaternary" => 'd7c9aa',
|
|
"header_background" => 'd7c9aa',
|
|
"header_primary" => '262322',
|
|
"highlight" => 'd7c9aa',
|
|
"danger" => 'db9584',
|
|
"success" => '78be78',
|
|
"love" => '8f6201'
|
|
},
|
|
# By @Flower_Child
|
|
'Summer': {
|
|
"primary" => '874342',
|
|
"secondary" => 'fffff4',
|
|
"tertiary" => 'fe9896',
|
|
"quaternary" => 'fcc9d0',
|
|
"header_background" => '96ccbf',
|
|
"header_primary" => 'fff1e7',
|
|
"highlight" => 'f3c07f',
|
|
"danger" => 'cfebdc',
|
|
"success" => 'fcb4b5',
|
|
"love" => 'f3c07f'
|
|
},
|
|
# By @Flower_Child
|
|
'Dark Rose': {
|
|
"primary" => 'ca9cb2',
|
|
"secondary" => '3a2a37',
|
|
"tertiary" => 'fdd459',
|
|
"quaternary" => '7e566a',
|
|
"header_background" => 'a97189',
|
|
"header_primary" => 'd9b2bb',
|
|
"highlight" => '6c3e63',
|
|
"danger" => '6c3e63',
|
|
"success" => 'd9b2bb',
|
|
"love" => 'd9b2bb'
|
|
}
|
|
}
|
|
|
|
# rubocop:enable Layout/HashAlignment
|
|
|
|
LIGHT_THEME_ID = 'Light'
|
|
|
|
def self.base_color_scheme_colors
|
|
base_with_hash = {}
|
|
|
|
base_colors.each do |name, color|
|
|
base_with_hash[name] = "#{color}"
|
|
end
|
|
|
|
list = [
|
|
{ id: LIGHT_THEME_ID, colors: base_with_hash }
|
|
]
|
|
|
|
CUSTOM_SCHEMES.each do |k, v|
|
|
list.push(id: k.to_s, colors: v)
|
|
end
|
|
|
|
list
|
|
end
|
|
|
|
def self.hex_cache
|
|
@hex_cache ||= DistributedCache.new("scheme_hex_for_name")
|
|
end
|
|
|
|
attr_accessor :is_base
|
|
|
|
has_many :color_scheme_colors, -> { order('id ASC') }, dependent: :destroy
|
|
|
|
alias_method :colors, :color_scheme_colors
|
|
|
|
before_save :bump_version
|
|
after_save :publish_discourse_stylesheet
|
|
after_save :dump_hex_cache
|
|
after_destroy :dump_hex_cache
|
|
belongs_to :theme
|
|
|
|
validates_associated :color_scheme_colors
|
|
|
|
BASE_COLORS_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/colors.scss"
|
|
COLOR_TRANSFORMATION_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/color_transformations.scss"
|
|
|
|
@mutex = Mutex.new
|
|
|
|
def self.base_colors
|
|
return @base_colors if @base_colors
|
|
@mutex.synchronize do
|
|
return @base_colors if @base_colors
|
|
base_colors = {}
|
|
File.readlines(BASE_COLORS_FILE).each do |line|
|
|
matches = /\$([\w]+):\s*#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})(?:[;]|\s)/.match(line.strip)
|
|
base_colors[matches[1]] = matches[2] if matches
|
|
end
|
|
@base_colors = base_colors
|
|
end
|
|
@base_colors
|
|
end
|
|
|
|
def self.color_transformation_variables
|
|
return @transformation_variables if @transformation_variables
|
|
@mutex.synchronize do
|
|
return @transformation_variables if @transformation_variables
|
|
transformation_variables = []
|
|
File.readlines(COLOR_TRANSFORMATION_FILE).each do |line|
|
|
matches = /\$([\w\-_]+):.*/.match(line.strip)
|
|
transformation_variables.append(matches[1]) if matches
|
|
end
|
|
@transformation_variables = transformation_variables
|
|
end
|
|
@transformation_variables
|
|
end
|
|
|
|
def self.base_color_schemes
|
|
base_color_scheme_colors.map do |hash|
|
|
scheme = new(name: I18n.t("color_schemes.#{hash[:id].downcase.gsub(' ', '_')}"), base_scheme_id: hash[:id])
|
|
scheme.colors = hash[:colors].map { |k, v| { name: k.to_s, hex: v.sub("#", "") } }
|
|
scheme.is_base = true
|
|
scheme
|
|
end
|
|
end
|
|
|
|
def self.base
|
|
return @base_color_scheme if @base_color_scheme
|
|
@base_color_scheme = new(name: I18n.t('color_schemes.base_theme_name'))
|
|
@base_color_scheme.colors = base_colors.map { |name, hex| { name: name, hex: hex } }
|
|
@base_color_scheme.is_base = true
|
|
@base_color_scheme
|
|
end
|
|
|
|
def self.is_base?(scheme_name)
|
|
base_color_scheme_colors.map { |c| c[:id] }.include?(scheme_name)
|
|
end
|
|
|
|
# create_from_base will create a new ColorScheme that overrides Discourse's base color scheme with the given colors.
|
|
def self.create_from_base(params)
|
|
new_color_scheme = new(name: params[:name])
|
|
new_color_scheme.via_wizard = true if params[:via_wizard]
|
|
new_color_scheme.base_scheme_id = params[:base_scheme_id]
|
|
|
|
colors = CUSTOM_SCHEMES[params[:base_scheme_id].to_sym]&.map do |name, hex|
|
|
{ name: name, hex: hex }
|
|
end if params[:base_scheme_id]
|
|
colors ||= base.colors_hashes
|
|
|
|
# Override base values
|
|
params[:colors].each do |name, hex|
|
|
c = colors.find { |x| x[:name].to_s == name.to_s }
|
|
c[:hex] = hex
|
|
end if params[:colors]
|
|
|
|
new_color_scheme.colors = colors
|
|
new_color_scheme.save
|
|
new_color_scheme
|
|
end
|
|
|
|
def self.lookup_hex_for_name(name, scheme_id = nil)
|
|
enabled_color_scheme = find_by(id: scheme_id) if scheme_id
|
|
enabled_color_scheme ||= Theme.where(id: SiteSetting.default_theme_id).first&.color_scheme
|
|
(enabled_color_scheme || base).colors.find { |c| c.name == name }.try(:hex) || "nil"
|
|
end
|
|
|
|
def self.hex_for_name(name, scheme_id = nil)
|
|
cache_key = scheme_id ? name + "_#{scheme_id}" : name
|
|
hex_cache[cache_key] ||= lookup_hex_for_name(name, scheme_id)
|
|
hex_cache[cache_key] == "nil" ? nil : hex_cache[cache_key]
|
|
end
|
|
|
|
def colors=(arr)
|
|
@colors_by_name = nil
|
|
arr.each do |c|
|
|
self.color_scheme_colors << ColorSchemeColor.new(name: c[:name], hex: c[:hex])
|
|
end
|
|
end
|
|
|
|
def colors_by_name
|
|
@colors_by_name ||= self.colors.inject({}) { |sum, c| sum[c.name] = c; sum; }
|
|
end
|
|
|
|
def clear_colors_cache
|
|
@colors_by_name = nil
|
|
end
|
|
|
|
def colors_hashes
|
|
color_scheme_colors.map do |c|
|
|
{ name: c.name, hex: c.hex }
|
|
end
|
|
end
|
|
|
|
def base_colors
|
|
colors = nil
|
|
if base_scheme_id && base_scheme_id != "Light"
|
|
colors = CUSTOM_SCHEMES[base_scheme_id.to_sym]
|
|
end
|
|
colors || ColorScheme.base_colors
|
|
end
|
|
|
|
def resolved_colors
|
|
resolved = ColorScheme.base_colors.dup
|
|
if base_scheme_id && base_scheme_id != "Light"
|
|
if scheme = CUSTOM_SCHEMES[base_scheme_id.to_sym]
|
|
scheme.each do |name, value|
|
|
resolved[name] = value
|
|
end
|
|
end
|
|
end
|
|
colors.each do |c|
|
|
resolved[c.name] = c.hex
|
|
end
|
|
resolved
|
|
end
|
|
|
|
def publish_discourse_stylesheet
|
|
if self.id
|
|
Stylesheet::Manager.clear_color_scheme_cache!
|
|
|
|
theme_ids = Theme.where(color_scheme_id: self.id).pluck(:id)
|
|
if theme_ids.present?
|
|
Stylesheet::Manager.cache.clear
|
|
Theme.notify_theme_change(
|
|
theme_ids,
|
|
with_scheme: true,
|
|
clear_manager_cache: false,
|
|
all_themes: true
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def dump_hex_cache
|
|
self.class.hex_cache.clear
|
|
end
|
|
|
|
def bump_version
|
|
if self.id
|
|
self.version += 1
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: color_schemes
|
|
#
|
|
# id :integer not null, primary key
|
|
# name :string not null
|
|
# version :integer default(1), not null
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# via_wizard :boolean default(FALSE), not null
|
|
# base_scheme_id :string
|
|
# theme_id :integer
|
|
#
|