FEATURE: WCAG compliant color schemes (#10882)

Co-authored-by: Kris <kris.aubuchon@discourse.org>
This commit is contained in:
Penar Musaraj 2020-10-15 14:05:48 -04:00 committed by GitHub
parent d68ad82a9e
commit 5763309953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 408 additions and 6 deletions

View File

@ -0,0 +1,307 @@
// Overrides for WCAG color schemes only
// Global
::placeholder {
color: var(--primary-medium);
opacity: 1;
}
.discourse-no-touch {
.btn-default,
.btn-icon {
&.btn-default {
.d-icon {
color: var(--primary-medium);
}
}
&:hover,
&.btn-hover {
.d-icon {
color: var(--secondary);
}
}
}
.btn-primary .d-icon {
color: var(--secondary);
}
.btn-icon.ok,
.btn-icon.cancel,
.btn-danger {
.d-icon {
color: var(--secondary);
}
}
.btn-flat.delete.d-hover {
background: var(--danger);
}
}
.nav-pills > li > a:not(.active):hover {
background: var(--tertiary-low);
color: var(--primary);
}
// Composer
#reply-control .reply-to .reply-details .d-icon {
opacity: 1;
color: var(--primary-low-mid);
}
.d-editor-button-bar {
.btn-icon.btn-default .d-icon {
color: var(--primary-low-mid);
.discourse-no-touch & {
&:hover {
color: var(--secondary);
}
}
}
}
// Site header
.menu-panel li a.widget-link:hover,
.menu-panel li a.widget-link:focus,
.menu-panel li.heading a.widget-link:hover,
.menu-panel li.heading a.widget-link:focus {
color: var(--primary);
background-color: var(--highlight-medium);
.d-icon {
color: var(--primary);
}
}
.menu-panel .panel-body-bottom .btn:hover {
.d-icon {
color: var(--primary);
}
}
.d-header-icons .d-icon {
color: var(--primary-low-mid);
}
.d-header-icons .icon:hover .d-icon,
.d-header-icons .icon:focus .d-icon {
color: var(--primary-high);
}
.d-header-icons .unread-notifications {
background: var(--tertiary);
}
// Topic list
table th {
color: var(--primary-medium);
}
.coldmap {
&-high {
color: #6c77cc !important;
}
&-med {
color: #548eaa !important;
}
&-low {
color: #32a1a5 !important;
}
}
.heatmap-high,
.heatmap-high a {
color: #dc3249 !important;
}
.heatmap-med,
.heatmap-med a {
color: #ae5b54 !important;
}
.heatmap-low,
.heatmap-low a {
color: #8f6d5b !important;
}
.badge-notification {
background: var(--primary-medium);
}
.badge-notification.new-posts,
.badge-notification.unread-posts {
background: var(--tertiary);
}
.select-kit.dropdown-select-box.period-chooser
.period-chooser-header
h2.selected-name
.top-date-string,
.select-kit.dropdown-select-box.period-chooser
.period-chooser-row
.top-date-string {
color: var(--primary-high);
}
// Posts
.discourse-no-touch .topic-body .actions .fade-out {
opacity: 1;
}
.topic-body .reply-to-tab {
color: var(--primary-medium);
.d-icon {
color: var(--primary-low-mid);
}
}
.timeline-container .topic-timeline {
.timeline-scrollarea {
border-color: var(--primary-low-mid);
}
.timeline-handle {
background: var(--primary-low-mid);
}
}
.topic-map h4 {
color: var(--primary);
}
.quote-controls,
.quote-controls .d-icon {
color: var(--primary-medium);
}
blockquote {
a,
a:visited {
color: var(--tertiary);
}
}
.meta .d-icon + .filename,
.meta .informations {
color: var(--secondary);
}
#topic-footer-buttons .bookmark.bookmarked:hover .d-icon-bookmark {
color: var(--secondary);
}
.gap {
color: var(--primary-medium);
}
.badge-notification.clicks {
color: var(--primary-high);
}
.topic-map {
background: transparent;
}
// Post controls
nav.post-controls {
// this is a bit tedious
a,
button {
color: var(--primary-medium);
.d-icon {
color: var(--primary-low-mid);
}
.discourse-no-touch & {
&:hover {
color: var(--secondary);
background: var(--primary-medium);
.d-icon {
color: var(--secondary);
}
}
}
&:focus {
background: var(--primary-medium);
}
}
.discourse-no-touch & {
.double-button:hover {
button {
background: var(--primary-medium);
color: var(--secondary);
.d-icon {
color: var(--secondary);
}
&.has-like {
.d-icon {
color: var(--secondary);
}
}
}
}
}
button.bookmark.bookmarked.d-hover .d-icon {
color: var(--secondary);
}
.double-button button.button-count + .toggle-like.d-hover {
background: var(--primary-medium);
.d-icon {
color: var(--love-low);
}
}
.discourse-no-touch & {
.double-button button.button-count.d-hover {
background: var(--primary-medium);
color: var(--secondary);
}
}
button.create {
.d-icon {
color: var(--primary-low-mid);
}
&.d-hover {
color: var(--secondary);
.d-icon {
color: var(--secondary);
}
}
}
.actions a,
.actions button {
color: var(--primary-medium);
}
}
nav.post-controls
.actions
.double-button
button.button-count
+ .toggle-like.d-hover {
background: var(--primary-medium);
}
.topic-admin-menu-button-container,
.timeline-controls {
.btn .d-icon {
// admin wrenches
color: var(--primary-medium);
}
}
// Categories
.list-cell,
.table-heading,
.category-list td,
.category-list th {
color: var(--primary-medium);
}
// Admin
.admin-controls {
.nav-pills > li > a:not(.active):hover {
background: var(--primary-medium);
color: var(--secondary);
}
}

View File

@ -94,6 +94,37 @@ class ColorScheme < ActiveRecord::Base
"danger" => '6c3e63',
"success" => 'd9b2bb',
"love" => 'd9b2bb'
},
"WCAG": {
"primary" => '000000',
"primary-medium" => '696969',
"primary-low-mid" => '909090',
"secondary" => 'ffffff',
"tertiary" => '3369FF',
"quaternary" => '3369FF',
"header_background" => 'ffffff',
"header_primary" => '000000',
"highlight" => '3369FF',
"highlight-high" => '0036E6',
"highlight-medium" => 'e0e9ff',
"highlight-low" => 'e0e9ff',
"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',
"danger" => 'BB1122',
"success" => '3d854d',
"love" => '9D256B'
}
}
@ -307,6 +338,10 @@ class ColorScheme < ActiveRecord::Base
primary_b > secondary_b
end
def is_wcag?
base_scheme_id&.start_with?('WCAG')
end
# Equivalent to dc-color-brightness() in variables.scss
def brightness(color)
rgb = color.scan(/../).map { |c| c.to_i(16) }

View File

@ -4058,6 +4058,12 @@ en:
primary:
name: "primary"
description: "Most text, icons, and borders."
primary-medium:
name: "primary-medium"
description: ""
primary-low-mid:
name: "primary-low-mid"
description: ""
secondary:
name: "secondary"
description: "The main background color, and text color of some buttons."
@ -4076,6 +4082,15 @@ en:
highlight:
name: "highlight"
description: "The background color of highlighted elements on the page, such as posts and topics."
highlight-high:
name: "highlight-high"
description: ""
highlight-medium:
name: "highlight-medium"
description: ""
highlight-low:
name: "highlight-low"
description: ""
danger:
name: "danger"
description: "Highlight color for actions like deleting posts and topics."

View File

@ -3923,6 +3923,10 @@ en:
latte: "Latte"
summer: "Summer"
dark_rose: "Dark Rose"
wcag: "WCAG Light"
wcag_theme_name: "WCAG Light"
wcag_dark: "WCAG Dark"
wcag_dark_theme_name: "WCAG Dark"
default_theme_name: "Default"
light_theme_name: "Light"
dark_theme_name: "Dark"

View File

@ -4,9 +4,16 @@
if !Theme.exists?
STDERR.puts "> Seeding theme and color schemes"
name = I18n.t("color_schemes.dark_theme_name")
dark_scheme = ColorScheme.find_by(base_scheme_id: "Dark")
dark_scheme ||= ColorScheme.create_from_base(name: name, via_wizard: true, base_scheme_id: "Dark", user_selectable: true)
color_schemes = [
{ name: I18n.t("color_schemes.dark"), base_scheme_id: "Dark" },
{ name: I18n.t("color_schemes.wcag"), base_scheme_id: "WCAG" },
{ name: I18n.t("color_schemes.wcag_dark"), base_scheme_id: "WCAG Dark" }
]
color_schemes.each do |cs|
scheme = ColorScheme.find_by(base_scheme_id: cs[:base_scheme_id])
scheme ||= ColorScheme.create_from_base(name: cs[:name], via_wizard: true, base_scheme_id: cs[:base_scheme_id], user_selectable: true)
end
name = I18n.t('color_schemes.default_theme_name')
default_theme = Theme.create!(name: name, user_id: -1)

View File

@ -22,6 +22,7 @@ module Stylesheet
if asset.to_s == Stylesheet::Manager::COLOR_SCHEME_STYLESHEET
file += Stylesheet::Importer.import_color_definitions(options[:theme_id])
file += Stylesheet::Importer.import_wcag_overrides(options[:color_scheme_id])
end
end

View File

@ -191,6 +191,13 @@ module Stylesheet
contents
end
def self.import_wcag_overrides(color_scheme_id)
if color_scheme_id && ColorScheme.find_by_id(color_scheme_id)&.is_wcag?
return "@import \"wcag\";"
end
""
end
def initialize(options)
@theme = options[:theme]
@theme_id = options[:theme_id]

View File

@ -187,6 +187,17 @@ describe Stylesheet::Importer do
styles = Stylesheet::Importer.import_color_definitions(nil)
expect(styles).to include(scss)
end
end
context "#import_wcag_overrides" do
it "should do nothing on a regular scheme" do
scheme = ColorScheme.create_from_base(name: 'Regular')
expect(Stylesheet::Importer.import_wcag_overrides(scheme.id)).to eq("")
end
it "should include WCAG overrides for WCAG based scheme" do
scheme = ColorScheme.create_from_base(name: 'WCAG New', base_scheme_id: "WCAG Dark")
expect(Stylesheet::Importer.import_wcag_overrides(scheme.id)).to eq("@import \"wcag\";")
end
end
end

View File

@ -102,4 +102,14 @@ describe ColorScheme do
expect(scheme.is_dark?).to eq(nil)
end
end
describe "is_wcag?" do
it "works as expected" do
expect(ColorScheme.create_from_base(name: 'Nope').is_wcag?).to eq(nil)
expect(ColorScheme.create_from_base(name: 'Nah', base_scheme_id: "Dark").is_wcag?).to eq(false)
expect(ColorScheme.create_from_base(name: 'Yup', base_scheme_id: "WCAG").is_wcag?).to eq(true)
expect(ColorScheme.create_from_base(name: 'Yup', base_scheme_id: "WCAG Dark").is_wcag?).to eq(true)
end
end
end

View File

@ -32,14 +32,19 @@ describe SiteSerializer do
it "includes user-selectable color schemes" do
# it includes seeded color schemes
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
expect(serialized[:user_color_schemes].count).to eq(1)
expect(serialized[:user_color_schemes].count).to eq(3)
dark_scheme = ColorScheme.create_from_base(name: "ADarkScheme", base_scheme_id: "Dark")
scheme_names = serialized[:user_color_schemes].map { |x| x[:name] }
expect(scheme_names).to include(I18n.t("color_schemes.dark"))
expect(scheme_names).to include(I18n.t("color_schemes.wcag"))
expect(scheme_names).to include(I18n.t("color_schemes.wcag_dark"))
dark_scheme = ColorScheme.create_from_base(name: "AnotherDarkScheme", base_scheme_id: "Dark")
dark_scheme.user_selectable = true
dark_scheme.save!
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
expect(serialized[:user_color_schemes].count).to eq(2)
expect(serialized[:user_color_schemes].count).to eq(4)
expect(serialized[:user_color_schemes][0][:is_dark]).to eq(true)
end