diff --git a/src/pretix/control/forms/__init__.py b/src/pretix/control/forms/__init__.py index ce1ea43da8..34e86e49e1 100644 --- a/src/pretix/control/forms/__init__.py +++ b/src/pretix/control/forms/__init__.py @@ -5,7 +5,6 @@ from django import forms from django.conf import settings from django.core.exceptions import ValidationError from django.forms.utils import from_current_timezone -from django.utils.deconstruct import deconstructible from django.utils.html import conditional_escape from django.utils.translation import ugettext_lazy as _ @@ -186,44 +185,3 @@ class SplitDateTimeField(forms.SplitDateTimeField): result = datetime.datetime.combine(*data_list) return from_current_timezone(result) 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) - ) diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 705d5d7c9b..ffe264a3d9 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -22,9 +22,8 @@ from pretix.base.models.event import EventMetaValue, SubEvent from pretix.base.reldate import RelativeDateField, RelativeDateTimeField from pretix.base.settings import PERSON_NAME_SCHEMES from pretix.control.forms import ( - ColorContrastValidator, ExtFileField, MultipleLanguagesWidget, - SingleLanguageWidget, SlugWidget, SplitDateTimeField, - SplitDateTimePickerWidget, + ExtFileField, MultipleLanguagesWidget, SingleLanguageWidget, SlugWidget, + SplitDateTimeField, SplitDateTimePickerWidget, ) from pretix.multidomain.urlreverse import build_absolute_uri from pretix.plugins.banktransfer.payment import BankTransfer @@ -936,37 +935,30 @@ class MailSettingsForm(SettingsForm): class DisplaySettingsForm(SettingsForm): primary_color = forms.CharField( 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, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) theme_color_success = forms.CharField( 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 " - "ground."), + help_text=_("We strongly suggest to use a shade of green."), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) theme_color_danger = forms.CharField( 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 " - "ground."), + help_text=_("We strongly suggest to use a dark shade of red."), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index 51fb4b8ba5..91aa00455f 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -10,9 +10,7 @@ from pretix.api.models import WebHook from pretix.api.webhooks import get_all_webhook_events from pretix.base.forms import I18nModelForm, SettingsForm from pretix.base.models import Device, Organizer, Team -from pretix.control.forms import ( - ColorContrastValidator, ExtFileField, MultipleLanguagesWidget, -) +from pretix.control.forms import ExtFileField, MultipleLanguagesWidget from pretix.multidomain.models import KnownDomain from pretix.presale.style import get_fonts @@ -181,32 +179,27 @@ class OrganizerDisplaySettingsForm(SettingsForm): validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) theme_color_success = forms.CharField( 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 " - "ground."), + help_text=_("We strongly suggest to use a shade of green."), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) theme_color_danger = forms.CharField( 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 " - "ground."), + help_text=_("We strongly suggest to use a shade of red."), required=False, validators=[ RegexValidator(regex='^#[0-9a-fA-F]{6}$', message=_('Please enter the hexadecimal code of a color, e.g. #990000.')), - ColorContrastValidator() ], widget=forms.TextInput(attrs={'class': 'colorpickerfield'}) ) diff --git a/src/pretix/static/pretixcontrol/js/ui/main.js b/src/pretix/static/pretixcontrol/js/ui/main.js index 63c53d0f6f..431207489b 100644 --- a/src/pretix/static/pretixcontrol/js/ui/main.js +++ b/src/pretix/static/pretixcontrol/js/ui/main.js @@ -157,6 +157,22 @@ var form_handlers = function (el) { 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({ format: 'hex', align: 'left', @@ -173,6 +189,32 @@ var form_handlers = function (el) { 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("
"); + } + var $note = $(this).parent().find(".contrast-state"); + if ($(this).val() === "") { + $note.remove(); + } + if (c > 7) { + $note.html("") + .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("") + .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("") + .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 () {