mirror of
https://github.com/discourse/discourse.git
synced 2025-02-06 19:38:24 +00:00
3135f472e2
This commit contains various quality improvements to our site setup wizard, along with some rearrangement of steps to improve the admin setup experience and encourage admins to customize the site early to avoid "all sites look the same" sentiment. #### Step rearrangement * “Your site is ready” from 3 → 4 * “Logos” from 4 → 5 * “Look and feel” from 5 → 3 #### Font selector improvements Changes the wizard font selector dropdown to show a preview of all fonts with a CSS class so you don't have to choose the font to get a preview. Also makes the fonts appear in alphabetical order. #### Preview improvements Placeholder text changed from lorem ipsum to actual topic titles, category names, and post content. This makes it feel more "real". Fixes "undefined" categories. Added a date to the topic timeline. Fixes button rectangles and other UI elements not changing in size when the font changed, leading to cut off text which looked super messy. Also fixed some font color issues. Fixed table header alignment for Latest topic list. #### Homepage style selector improvements Limited the big list of homepage styles to Latest, Hot, Categories with latest topics, and Category boxes based on research into the most common options. #### Preview header Changed the preview header to move the hamburger to the left and add a chat icon #### And more! Changed the background of the wizard to use our branded blob style.
534 lines
15 KiB
Ruby
534 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ColorScheme < ActiveRecord::Base
|
|
BUILT_IN_SCHEMES = {
|
|
Dark: {
|
|
"primary" => "dddddd",
|
|
"secondary" => "222222",
|
|
"tertiary" => "099dd7",
|
|
"quaternary" => "c14924",
|
|
"header_background" => "111111",
|
|
"header_primary" => "dddddd",
|
|
"highlight" => "a87137",
|
|
"selected" => "052e3d",
|
|
"hover" => "313131",
|
|
"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",
|
|
"selected" => "e6e6e6",
|
|
"hover" => "f0f0f0",
|
|
"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",
|
|
"selected" => "272727",
|
|
"hover" => "2F2F30",
|
|
"danger" => "e45735",
|
|
"success" => "fdd459",
|
|
"love" => "fdd459",
|
|
},
|
|
# By @rafafotes
|
|
"Shades of Blue": {
|
|
"primary" => "203243",
|
|
"secondary" => "eef4f7",
|
|
"tertiary" => "416376",
|
|
"quaternary" => "5e99b9",
|
|
"header_background" => "86bddb",
|
|
"header_primary" => "203243",
|
|
"highlight" => "86bddb",
|
|
"selected" => "bee0f2",
|
|
"hover" => "d2efff",
|
|
"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",
|
|
"selected" => "3e2a14",
|
|
"hover" => "4c3319",
|
|
"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",
|
|
"selected" => "f5eaea",
|
|
"hover" => "f9f3f3",
|
|
"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" => "bd36a3",
|
|
"selected" => "2a1620",
|
|
"hover" => "331b27",
|
|
"danger" => "6c3e63",
|
|
"success" => "d9b2bb",
|
|
"love" => "d9b2bb",
|
|
},
|
|
WCAG: {
|
|
"primary" => "000000",
|
|
"primary-medium" => "696969",
|
|
"primary-low-mid" => "909090",
|
|
"secondary" => "ffffff",
|
|
"tertiary" => "0033CC",
|
|
"quaternary" => "3369FF",
|
|
"header_background" => "ffffff",
|
|
"header_primary" => "000000",
|
|
"highlight" => "ffff00",
|
|
"highlight-high" => "0036E6",
|
|
"highlight-medium" => "e0e9ff",
|
|
"highlight-low" => "e0e9ff",
|
|
"selected" => "E2E9FE",
|
|
"hover" => "F0F4FE",
|
|
"danger" => "BB1122",
|
|
"success" => "3d854d",
|
|
"love" => "9D256B",
|
|
},
|
|
"WCAG Dark": {
|
|
"primary" => "ffffff",
|
|
"primary-medium" => "999999",
|
|
"primary-low-mid" => "888888",
|
|
"secondary" => "0c0c0c",
|
|
"tertiary" => "759AFF",
|
|
"quaternary" => "759AFF",
|
|
"header_background" => "000000",
|
|
"header_primary" => "ffffff",
|
|
"highlight" => "3369FF",
|
|
"selected" => "0d2569",
|
|
"hover" => "002382",
|
|
"danger" => "FF697A",
|
|
"success" => "70B880",
|
|
"love" => "9D256B",
|
|
},
|
|
# By @zenorocha
|
|
Dracula: {
|
|
"primary_very_low" => "373A47",
|
|
"primary_low" => "414350",
|
|
"primary_low_mid" => "8C8D94",
|
|
"primary_medium" => "A3A4AA",
|
|
"primary_high" => "CCCCCF",
|
|
"primary" => "f2f2f2",
|
|
"primary-50" => "3F414E",
|
|
"primary-100" => "535460",
|
|
"primary-200" => "666972",
|
|
"primary-300" => "7A7C84",
|
|
"primary-400" => "8D8F96",
|
|
"primary-500" => "A2A3A9",
|
|
"primary-600" => "B6B7BC",
|
|
"primary-700" => "C7C7C7",
|
|
"primary-800" => "DEDFE0",
|
|
"primary-900" => "F5F5F5",
|
|
"secondary_low" => "CCCCCF",
|
|
"secondary_medium" => "91939A",
|
|
"secondary_high" => "6A6C76",
|
|
"secondary_very_high" => "3D404C",
|
|
"secondary" => "2d303e",
|
|
"tertiary_low" => "4A4463",
|
|
"tertiary_medium" => "6E5D92",
|
|
"tertiary" => "bd93f9",
|
|
"tertiary_high" => "9275C1",
|
|
"quaternary_low" => "6AA8BA",
|
|
"quaternary" => "8be9fd",
|
|
"header_background" => "373A47",
|
|
"header_primary" => "f2f2f2",
|
|
"highlight_low" => "686D55",
|
|
"highlight_medium" => "52592B",
|
|
"highlight_high" => "C0C879",
|
|
"selected" => "4A4463",
|
|
"hover" => "61597f",
|
|
"danger_low" => "957279",
|
|
"danger" => "ff5555",
|
|
"success_low" => "386D50",
|
|
"success_medium" => "44B366",
|
|
"success" => "50fa7b",
|
|
"love_low" => "6C4667",
|
|
"love" => "ff79c6",
|
|
},
|
|
# By @altercation
|
|
"Solarized Light": {
|
|
"primary_very_low" => "F0ECD7",
|
|
"primary_low" => "D6D8C7",
|
|
"primary_low_mid" => "A4AFA5",
|
|
"primary_medium" => "7E918C",
|
|
"primary_high" => "4C6869",
|
|
"primary" => "002B36",
|
|
"primary-50" => "F0EBDA",
|
|
"primary-100" => "DAD8CA",
|
|
"primary-200" => "B2B9B3",
|
|
"primary-300" => "839496",
|
|
"primary-400" => "76898C",
|
|
"primary-500" => "697F83",
|
|
"primary-600" => "627A7E",
|
|
"primary-700" => "556F74",
|
|
"primary-800" => "415F66",
|
|
"primary-900" => "21454E",
|
|
"secondary_low" => "325458",
|
|
"secondary_medium" => "6C8280",
|
|
"secondary_high" => "97A59D",
|
|
"secondary_very_high" => "E8E6D3",
|
|
"secondary" => "FCF6E1",
|
|
"tertiary_low" => "D6E6DE",
|
|
"tertiary_medium" => "7EBFD7",
|
|
"tertiary" => "0088cc",
|
|
"tertiary_high" => "329ED0",
|
|
"quaternary" => "e45735",
|
|
"header_background" => "FCF6E1",
|
|
"header_primary" => "002B36",
|
|
"highlight_low" => "FDF9AD",
|
|
"highlight_medium" => "E3D0A3",
|
|
"highlight" => "F2F481",
|
|
"highlight_high" => "BCAA7F",
|
|
"selected" => "E8E6D3",
|
|
"hover" => "F0EBDA",
|
|
"danger_low" => "F8D9C2",
|
|
"danger" => "e45735",
|
|
"success_low" => "CFE5B9",
|
|
"success_medium" => "4CB544",
|
|
"success" => "009900",
|
|
"love_low" => "FCDDD2",
|
|
"love" => "fa6c8d",
|
|
},
|
|
# By @altercation
|
|
"Solarized Dark": {
|
|
"primary_very_low" => "0D353F",
|
|
"primary_low" => "193F47",
|
|
"primary_low_mid" => "798C88",
|
|
"primary_medium" => "97A59D",
|
|
"primary_high" => "B5BDB1",
|
|
"primary" => "FCF6E1",
|
|
"primary-50" => "21454E",
|
|
"primary-100" => "415F66",
|
|
"primary-200" => "556F74",
|
|
"primary-300" => "627A7E",
|
|
"primary-400" => "697F83",
|
|
"primary-500" => "76898C",
|
|
"primary-600" => "839496",
|
|
"primary-700" => "B2B9B3",
|
|
"primary-800" => "DAD8CA",
|
|
"primary-900" => "F0EBDA",
|
|
"secondary_low" => "B5BDB1",
|
|
"secondary_medium" => "81938D",
|
|
"secondary_high" => "4E6A6B",
|
|
"secondary_very_high" => "143B44",
|
|
"secondary" => "002B36",
|
|
"tertiary_low" => "003E54",
|
|
"tertiary_medium" => "00557A",
|
|
"tertiary" => "1a97d5",
|
|
"tertiary_high" => "006C9F",
|
|
"quaternary_low" => "944835",
|
|
"quaternary" => "e45735",
|
|
"header_background" => "002B36",
|
|
"header_primary" => "FCF6E1",
|
|
"highlight_low" => "4D6B3D",
|
|
"highlight_medium" => "464C33",
|
|
"highlight" => "F2F481",
|
|
"highlight_high" => "BFCA47",
|
|
"selected" => "143B44",
|
|
"hover" => "21454E",
|
|
"danger_low" => "443836",
|
|
"danger_medium" => "944835",
|
|
"danger" => "e45735",
|
|
"success_low" => "004C26",
|
|
"success_medium" => "007313",
|
|
"success" => "009900",
|
|
"love_low" => "4B3F50",
|
|
"love" => "fa6c8d",
|
|
},
|
|
}
|
|
|
|
LIGHT_THEME_ID = "Light"
|
|
|
|
def self.base_color_scheme_colors
|
|
base_with_hash = []
|
|
|
|
base_colors.each { |name, color| base_with_hash << { name: name, hex: "#{color}" } }
|
|
|
|
list = [{ id: LIGHT_THEME_ID, colors: base_with_hash }]
|
|
|
|
BUILT_IN_SCHEMES.each do |k, v|
|
|
colors = []
|
|
v.each { |name, color| colors << { name: name, hex: "#{color}" } }
|
|
list.push(id: k.to_s, colors: colors)
|
|
end
|
|
|
|
list
|
|
end
|
|
|
|
def self.hex_cache
|
|
@hex_cache ||= DistributedCache.new("scheme_hex_for_name")
|
|
end
|
|
|
|
attr_accessor :is_base
|
|
attr_accessor :skip_publish
|
|
|
|
has_many :color_scheme_colors, -> { order("id ASC") }, dependent: :destroy
|
|
|
|
alias_method :colors, :color_scheme_colors
|
|
|
|
before_save :bump_version
|
|
after_save_commit :publish_discourse_stylesheet, unless: :skip_publish
|
|
after_save_commit :dump_caches
|
|
after_destroy :dump_caches
|
|
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| { name: k[:name], hex: k[:hex] } }
|
|
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]
|
|
new_color_scheme.user_selectable = true
|
|
|
|
colors =
|
|
BUILT_IN_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.skip_publish if params[:skip_publish]
|
|
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)
|
|
end
|
|
|
|
def self.hex_for_name(name, scheme_id = nil)
|
|
hex_cache.defer_get_set(scheme_id ? name + "_#{scheme_id}" : name) do
|
|
lookup_hex_for_name(name, scheme_id)
|
|
end
|
|
end
|
|
|
|
def colors=(arr)
|
|
@colors_by_name = nil
|
|
arr.each { |c| self.color_scheme_colors << ColorSchemeColor.new(name: c[:name], hex: c[:hex]) }
|
|
end
|
|
|
|
def colors_by_name
|
|
@colors_by_name ||=
|
|
self
|
|
.colors
|
|
.inject({}) do |sum, c|
|
|
sum[c.name] = c
|
|
sum
|
|
end
|
|
end
|
|
|
|
def clear_colors_cache
|
|
@colors_by_name = nil
|
|
end
|
|
|
|
def colors_hashes
|
|
color_scheme_colors.map { |c| { name: c.name, hex: c.hex } }
|
|
end
|
|
|
|
def base_colors
|
|
colors = nil
|
|
colors = BUILT_IN_SCHEMES[base_scheme_id.to_sym] if base_scheme_id && base_scheme_id != "Light"
|
|
colors || ColorScheme.base_colors
|
|
end
|
|
|
|
def resolved_colors
|
|
from_base = ColorScheme.base_colors
|
|
from_custom_scheme = base_colors
|
|
from_db = colors.map { |c| [c.name, c.hex] }.to_h
|
|
|
|
resolved = from_base.merge(from_custom_scheme).except("hover", "selected").merge(from_db)
|
|
|
|
# Equivalent to primary-100 in light mode, or primary-low in dark mode
|
|
resolved["hover"] ||= ColorMath.dark_light_diff(
|
|
resolved["primary"],
|
|
resolved["secondary"],
|
|
0.94,
|
|
-0.78,
|
|
)
|
|
|
|
# Equivalent to primary-low in light mode, or primary-100 in dark mode
|
|
resolved["selected"] ||= ColorMath.dark_light_diff(
|
|
resolved["primary"],
|
|
resolved["secondary"],
|
|
0.9,
|
|
-0.8,
|
|
)
|
|
|
|
resolved
|
|
end
|
|
|
|
def publish_discourse_stylesheet
|
|
self.class.publish_discourse_stylesheets!(self.id) if self.id
|
|
end
|
|
|
|
def self.publish_discourse_stylesheets!(id = nil)
|
|
Stylesheet::Manager.clear_color_scheme_cache!
|
|
|
|
theme_ids = []
|
|
if id
|
|
theme_ids = Theme.where(color_scheme_id: id).pluck(:id)
|
|
else
|
|
theme_ids = Theme.all.pluck(:id)
|
|
end
|
|
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
|
|
|
|
def dump_caches
|
|
self.class.hex_cache.clear
|
|
ApplicationSerializer.expire_cache_fragment!("user_color_schemes")
|
|
end
|
|
|
|
def bump_version
|
|
self.version += 1 if self.id
|
|
end
|
|
|
|
def is_dark?
|
|
return if colors.to_a.empty?
|
|
|
|
primary_b = ColorMath.brightness(resolved_colors["primary"])
|
|
secondary_b = ColorMath.brightness(resolved_colors["secondary"])
|
|
|
|
primary_b > secondary_b
|
|
end
|
|
|
|
def is_wcag?
|
|
base_scheme_id&.start_with?("WCAG")
|
|
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
|
|
# user_selectable :boolean default(FALSE), not null
|
|
#
|