forked from CGM_Public/pretix_original
Only warn about bad-contrasat colors
This commit is contained in:
@@ -5,7 +5,6 @@ from django import forms
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.forms.utils import from_current_timezone
|
from django.forms.utils import from_current_timezone
|
||||||
from django.utils.deconstruct import deconstructible
|
|
||||||
from django.utils.html import conditional_escape
|
from django.utils.html import conditional_escape
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
@@ -186,44 +185,3 @@ class SplitDateTimeField(forms.SplitDateTimeField):
|
|||||||
result = datetime.datetime.combine(*data_list)
|
result = datetime.datetime.combine(*data_list)
|
||||||
return from_current_timezone(result)
|
return from_current_timezone(result)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@deconstructible
|
|
||||||
class ColorContrastValidator:
|
|
||||||
message = _('This color is too bright to allow for proper contrast when used for text on white ground.')
|
|
||||||
code = 'contrast'
|
|
||||||
|
|
||||||
def __init__(self, message=None, code=None):
|
|
||||||
if message is not None:
|
|
||||||
self.message = message
|
|
||||||
if code is not None:
|
|
||||||
self.code = code
|
|
||||||
|
|
||||||
def __call__(self, value):
|
|
||||||
# See https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
|
||||||
fg_color = [int(str(value)[1:3], 16), int(str(value)[1:3], 16), int(str(value)[1:3], 16)]
|
|
||||||
bg_color = [255, 255, 255]
|
|
||||||
fg_lum = [
|
|
||||||
(float(cc) / 255) / 12.92
|
|
||||||
if (float(cc) / 255) < 0.03928
|
|
||||||
else (((float(cc) / 255) + 0.055) / 1.055) ** 2.4
|
|
||||||
for cc in fg_color
|
|
||||||
]
|
|
||||||
bg_lum = [
|
|
||||||
(float(cc) / 255) / 12.92
|
|
||||||
if (float(cc) / 255) < 0.03928
|
|
||||||
else (((float(cc) / 255) + 0.055) / 1.055) ** 2.4
|
|
||||||
for cc in bg_color
|
|
||||||
]
|
|
||||||
fg_rel_lum = 0.2126 * fg_lum[0] + 0.7152 * fg_lum[1] + 0.0722 * fg_lum[2]
|
|
||||||
bg_rel_lum = 0.2126 * bg_lum[0] + 0.7152 * bg_lum[1] + 0.0722 * bg_lum[2]
|
|
||||||
contrast_ratio = (bg_rel_lum + 0.05) / (fg_rel_lum + 0.05)
|
|
||||||
if contrast_ratio < 1.4:
|
|
||||||
raise ValidationError(self.message, code=self.code)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return (
|
|
||||||
isinstance(other, ColorContrastValidator) and
|
|
||||||
(self.message == other.message) and
|
|
||||||
(self.code == other.code)
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ from pretix.base.models.event import EventMetaValue, SubEvent
|
|||||||
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
||||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||||
from pretix.control.forms import (
|
from pretix.control.forms import (
|
||||||
ColorContrastValidator, ExtFileField, MultipleLanguagesWidget,
|
ExtFileField, MultipleLanguagesWidget, SingleLanguageWidget, SlugWidget,
|
||||||
SingleLanguageWidget, SlugWidget, SplitDateTimeField,
|
SplitDateTimeField, SplitDateTimePickerWidget,
|
||||||
SplitDateTimePickerWidget,
|
|
||||||
)
|
)
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
from pretix.plugins.banktransfer.payment import BankTransfer
|
from pretix.plugins.banktransfer.payment import BankTransfer
|
||||||
@@ -936,37 +935,30 @@ class MailSettingsForm(SettingsForm):
|
|||||||
class DisplaySettingsForm(SettingsForm):
|
class DisplaySettingsForm(SettingsForm):
|
||||||
primary_color = forms.CharField(
|
primary_color = forms.CharField(
|
||||||
label=_("Primary color"),
|
label=_("Primary color"),
|
||||||
help_text=_("We strongly suggest to use a dark shade that has a good contrast for text on white ground."),
|
|
||||||
required=False,
|
required=False,
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
theme_color_success = forms.CharField(
|
theme_color_success = forms.CharField(
|
||||||
label=_("Accent color for success"),
|
label=_("Accent color for success"),
|
||||||
help_text=_("We strongly suggest to use a dark shade of green that has a good contrast for text on white "
|
help_text=_("We strongly suggest to use a shade of green."),
|
||||||
"ground."),
|
|
||||||
required=False,
|
required=False,
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
theme_color_danger = forms.CharField(
|
theme_color_danger = forms.CharField(
|
||||||
label=_("Accent color for errors"),
|
label=_("Accent color for errors"),
|
||||||
help_text=_("We strongly suggest to use a dark shade of red that has a good contrast for text on white "
|
help_text=_("We strongly suggest to use a dark shade of red."),
|
||||||
"ground."),
|
|
||||||
required=False,
|
required=False,
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
|
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ from pretix.api.models import WebHook
|
|||||||
from pretix.api.webhooks import get_all_webhook_events
|
from pretix.api.webhooks import get_all_webhook_events
|
||||||
from pretix.base.forms import I18nModelForm, SettingsForm
|
from pretix.base.forms import I18nModelForm, SettingsForm
|
||||||
from pretix.base.models import Device, Organizer, Team
|
from pretix.base.models import Device, Organizer, Team
|
||||||
from pretix.control.forms import (
|
from pretix.control.forms import ExtFileField, MultipleLanguagesWidget
|
||||||
ColorContrastValidator, ExtFileField, MultipleLanguagesWidget,
|
|
||||||
)
|
|
||||||
from pretix.multidomain.models import KnownDomain
|
from pretix.multidomain.models import KnownDomain
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
@@ -181,32 +179,27 @@ class OrganizerDisplaySettingsForm(SettingsForm):
|
|||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
theme_color_success = forms.CharField(
|
theme_color_success = forms.CharField(
|
||||||
label=_("Accent color for success"),
|
label=_("Accent color for success"),
|
||||||
help_text=_("We strongly suggest to use a dark shade of green that has a good contrast for text on white "
|
help_text=_("We strongly suggest to use a shade of green."),
|
||||||
"ground."),
|
|
||||||
required=False,
|
required=False,
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
theme_color_danger = forms.CharField(
|
theme_color_danger = forms.CharField(
|
||||||
label=_("Accent color for errors"),
|
label=_("Accent color for errors"),
|
||||||
help_text=_("We strongly suggest to use a dark shade of red that has a good contrast for text on white "
|
help_text=_("We strongly suggest to use a shade of red."),
|
||||||
"ground."),
|
|
||||||
required=False,
|
required=False,
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
|
||||||
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
|
||||||
|
|
||||||
ColorContrastValidator()
|
|
||||||
],
|
],
|
||||||
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -157,6 +157,22 @@ var form_handlers = function (el) {
|
|||||||
fill_field.on("dp.show", show);
|
fill_field.on("dp.show", show);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function luminanace(r, g, b) {
|
||||||
|
var a = [r, g, b].map(function (v) {
|
||||||
|
v /= 255;
|
||||||
|
return v <= 0.03928
|
||||||
|
? v / 12.92
|
||||||
|
: Math.pow( (v + 0.055) / 1.055, 2.4 );
|
||||||
|
});
|
||||||
|
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
|
||||||
|
}
|
||||||
|
function contrast(rgb1, rgb2) {
|
||||||
|
var l1 = luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05,
|
||||||
|
l2 = luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05,
|
||||||
|
ratio = l1/l2
|
||||||
|
if (l2 > l1) {ratio = 1/ratio}
|
||||||
|
return ratio.toFixed(1)
|
||||||
|
}
|
||||||
el.find(".colorpickerfield").colorpicker({
|
el.find(".colorpickerfield").colorpicker({
|
||||||
format: 'hex',
|
format: 'hex',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
@@ -173,6 +189,32 @@ var form_handlers = function (el) {
|
|||||||
maxTop: 200
|
maxTop: 200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).on('changeColor create', function (e) {
|
||||||
|
var rgb = $(this).colorpicker('color').toRGB();
|
||||||
|
var c = contrast([255,255,255], [rgb.r, rgb.g, rgb.b]);
|
||||||
|
var mark = 'times';
|
||||||
|
if ($(this).parent().find(".contrast-state").length === 0) {
|
||||||
|
$(this).parent().append("<div class='help-block contrast-state'></div>");
|
||||||
|
}
|
||||||
|
var $note = $(this).parent().find(".contrast-state");
|
||||||
|
if ($(this).val() === "") {
|
||||||
|
$note.remove();
|
||||||
|
}
|
||||||
|
if (c > 7) {
|
||||||
|
$note.html("<span class='fa fa-fw fa-check-circle'></span>")
|
||||||
|
.append(gettext('Your color has great contrast and is very easy to read!'));
|
||||||
|
$note.addClass("text-success").removeClass("text-warning").removeClass("text-danger");
|
||||||
|
} else if (c > 2.5) {
|
||||||
|
$note.html("<span class='fa fa-fw fa-info-circle'></span>")
|
||||||
|
.append(gettext('Your color has decent contrast and is probably good-enough to read!'));
|
||||||
|
$note.removeClass("text-success").removeClass("text-warning").removeClass("text-danger");
|
||||||
|
} else {
|
||||||
|
$note.html("<span class='fa fa-fw fa-warning'></span>")
|
||||||
|
.append(gettext('Your color has bad contrast for text on white background, please choose a darker ' +
|
||||||
|
'shade.'));
|
||||||
|
$note.addClass("text-danger").removeClass("text-success").removeClass("text-warning");
|
||||||
|
}
|
||||||
|
console.log(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
el.find("input[data-checkbox-dependency]").each(function () {
|
el.find("input[data-checkbox-dependency]").each(function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user