Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add warning for low contrast and equal colors #86

Closed
wants to merge 13 commits into from
Closed
1 change: 1 addition & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ The icons `assets/qr-icon*.svg` (used in `src/icons/icon-small-*.svg`) are made
* [`common/img/info-dark.svg`](common/img/info-dark.svg) by Mozilla, license: [MPL v2.0](https://www.mozilla.org/en-US/MPL/2.0/)
* [`common/img/info-light.svg`](common/img/info-light.svg) by Mozilla, license: [MPL v2.0](https://www.mozilla.org/en-US/MPL/2.0/)
* [`common/img/open-in-new.svg`](common/img/open-in-new.svg) by Mozilla, license: [MPL v2.0](https://www.mozilla.org/en-US/MPL/2.0/)
* [`common/img/warning-dark.svg`](common/img/warning-dark.svg) by Mozilla, license: [MPL v2.0](https://www.mozilla.org/en-US/MPL/2.0/)

The MPLv2.0 license is reproduced below:

Expand Down
16 changes: 16 additions & 0 deletions src/_locales/de/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@
"message": "Wir benötigen die Download-Berechtigung, um das Speichern des QR-Codes fortzusetzen.",
"description": "Shown, when the user is asked to allow the download permission to save the QR code."
},
"lowContrastRatioInfo": {
"message": "Der QR-Code kann wahrscheinlich nicht von allen Scannern erkannt werden, weil der Kontrast zu niedrig ist.",
"desription": "The message shown when the contrast ratio is too low"
},
"lowContrastRatioWarning": {
"message": "Der Kontrast zwischen der Farbe des QR-Codes und dessen Hintergrund ist zu niedrig um von den meisten QR-Code-Scannern erkannt zu werden",
"desription": "The warning shown when the contrast ratio is too low"
},
"lowContrastRatioError": {
"message": "Die Farbe des QR-Codes und des Hintergrunds ist zu ähnlich.",
"description": "The message shown when the color for the QR code and the background are equal"
},
"messageAutoSelectColorButton": {
"message": "Besten Kontrast berechnen",
"description": "The text of a button that sets a new color with a sufficient contrast."
},

// tips
"tipYouLikeAddon": {
Expand Down
16 changes: 16 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@
"message": "To continue to save the QR code, we need the download permission.",
"description": "Shown, when the user is asked to allow the download permission to save the QR code."
},
"lowContrastRatioInfo": {
"message": "Your QR code may be hard to scan by some QR code readers because the contrast is too low.",
"desription": "The message shown when the contrast ratio is too low"
},
"lowContrastRatioWarning": {
"message": "The contrast ratio between the QR code color and the background might be too low to be recognized by the most QR code readers",
"desription": "The warning shown when the contrast ratio is too low"
},
"lowContrastRatioError": {
"message": "The background and the foreground color are too similar.",
"description": "The message shown when the color for the QR code and the background are equal"
},
"messageAutoSelectColorButton": {
"message": "Calculate best contrast",
"description": "The text of a button that sets a new color with a sufficient contrast."
},

// tips
"tipYouLikeAddon": {
Expand Down
45 changes: 44 additions & 1 deletion src/common/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ a:active {

/* buttons https://design.firefox.com/photon/components/buttons.html */
.micro-button {
height: 24px;
min-height: 24px;
border-radius: 2px;

padding-left: 8px;
Expand All @@ -65,6 +65,20 @@ a:active {
padding-bottom: 2px;

box-sizing: content-box;

/* do not break over multiple lines */
/* white-space: nowrap; */
height: auto; /* currently, we rather prefer breaking until https://github.com/rugk/offline-qr-code/issues/12 is done */
}

/* use light color for dark backgrounds */
.micro-button:hover.success,
.micro-button:active.success,
.micro-button:hover.warning,
.micro-button:active.warning,
.micro-button:hover.error,
.micro-button:active.error {
color: var(--white-100);
}

.micro-button.info {
Expand All @@ -87,6 +101,27 @@ a:active {
background-color: var(--green-80);
}

.micro-button.warning {
background-color: var(--yellow-60);
}
.micro-button:hover.warning {
background-color: var(--yellow-70);
}
.micro-button:active.warning {
background-color: var(--yellow-80);
}

.micro-button.error {
background-color: var(--red-70);
color: var(--white-100);
}
.micro-button:hover.error {
background-color: var(--red-80);
}
.micro-button:active.error {
background-color: var(--red-90);
}

.micro-button:focus {
box-shadow: 0 0 0 1px #0a84ff inset, 0 0 0 1px #0a84ff, 0 0 0 4px rgba(10, 132, 255, 0.3)
}
Expand Down Expand Up @@ -150,6 +185,11 @@ a:active {
background-color: var(--green-50);
}

.warning {
color: var(--yellow-90);
background-color: var(--yellow-50);
}

/* message box action button */
.message-action-button {
margin-left: 8px;
Expand Down Expand Up @@ -188,6 +228,9 @@ a:active {
.success::before {
background-image: url('/common/img/check.svg');
}
.warning::before {
background-image: url('/common/img/warning-dark.svg');
}

.icon-dismiss {
box-sizing: content-box;
Expand Down
120 changes: 120 additions & 0 deletions src/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,126 @@ const RandomTips = (function () {// eslint-disable-line no-unused-vars
return me;
})();

const Colors = (() => { // eslint-disable-line no-unused-vars
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this module looks great! So for doing #20 later, everything is set.

const me = {};

/**
* Some breakpoints for specific color ratios.
*
* This includes definitions from WCAG and some custom ones.
*
* @name Colors.CONTRAST_RATIO
* @type {object} with integers
* @const
* @default
*/
me.CONTRAST_RATIO = {
WAY_TOO_LOW: 2,
// WCAG 2.1 AA text: https://www.w3.org/TR/WCAG/#contrast-minimum
LARGE_AA: 3.1,
SMALL_AA: 4.5,
// WCAG 2.1 AAA text: https://www.w3.org/TR/WCAG/#contrast-enhanced
LARGE_AAA: 4.5,
SMALL_AAA: 7.5,
// WCAG 2.1 AA non-text: https://www.w3.org/TR/WCAG/#non-text-contrast
NON_TEXT_AA: 3.1
};

/**
* Calculates the contrast between two colors
*
* @name Colors.contrastRatio
* @function
* @param {Array} rgb1
* @param {Array} rgb2
* @returns {int}
*/
me.contrastRatio = function(rgb1, rgb2) {
const l1 = luminance(rgb1);
const l2 = luminance(rgb2);
// Formula: https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
};

/**
* Calculates the luminance of a given RGB color.
*
* @name Colors.luminance
* @function
* @private
* @param {Array} rgb
* @returns {Array|null}
*/
function luminance(rgb) {
// Formula: https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
let r = rgb[0] / 255;
let g = rgb[1] / 255;
let b = rgb[2] / 255;
// Note: I'm using 0.04045 here (instead of 0.03928) because 0.04045 is the
// number used in the actual sRGB standard.
// See also: https://github.com/w3c/wcag21/issues/815
r = r < 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
g = g < 0.04045 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
b = b < 0.04045 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/**
* Returns the complementary color of a given RGB array.
*
* @name Colors.invertColor
* @function
* @param {Array} rgb
* @returns {string}
*/
me.invertColor = function(rgb) {
// invert color components
const r = (255 - rgb[0]).toString(16);
const g = (255 - rgb[1]).toString(16);
const b = (255 - rgb[2]).toString(16);
// pad each with zeros and return
return `#${padZero(r)}${padZero(g)}${padZero(b)}`;
};

/**
* Adds missing zeros in front of a string.
*
* @name Colors.padZero
* @function
* @private
* @param {string} string
* @param {int} length
* @returns {string}
*/
function padZero(string, length) {
length = length || 2;
const zeros = new Array(length).join("0");
return (zeros + string).slice(-length);
}

/**
* Converts a hex color string to RGB.
*
* @name Colors.hexToRgb
* @function
* @param {string} hex
* @returns {Array|null}
*/
me.hexToRgb = function(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (result) {
return [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
];
}
return null;
};

return me;
})();

// init modules
AddonSettings.loadOptions();
Logger.init();
Expand Down
6 changes: 6 additions & 0 deletions src/common/img/warning-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/common/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
--green-80: #006504;
--green-90: #003706;

--yellow-50: #ffe900;
--yellow-60: #d7b600;
--yellow-70: #a47f00;
--yellow-80: #715100;
--yellow-90: #3e2800;

--red-50: #ff0039;
--red-60: #d70022;
--red-70: #a4000f;
--red-80: #5a0002;
--red-90: #3e0200;

--red-60: #d70022;

--grey-20: #ededf0;
Expand Down
2 changes: 1 addition & 1 deletion src/options.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
body {
body, .micro-button {
/* the style the other options in the settings pane use */
font-size: 1.25rem; /* TODO: adjust to font size on mobile */
color: #333;
Expand Down
13 changes: 13 additions & 0 deletions src/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
</div>
<div id="messageInfo" class="message-box info invisible fade-hide">
<span class="message-text">Some settings are managed by your administrator and cannot be changed.</span>
<a href="#">
<button class="message-action-button micro-button info invisible"></button>
</a>
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
</div>
<div id="messageSuccess" class="message-box success invisible fade-hide">
Expand All @@ -33,6 +36,16 @@
</div>
<div id="messageError" class="message-box error invisible fade-hide">
<span class="message-text">An error happened.</span>
<a href="#">
<button class="message-action-button micro-button error invisible"></button>
</a>
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
</div>
<div id="messageWarning" class="message-box warning invisible fade-hide">
<span class="message-text">There were some difficulties.</span>
<a href="#">
<button class="message-action-button micro-button warning invisible"></button>
</a>
<img class="icon-dismiss invisible" src="/common/img/close.svg" width="24" height="24" tabindex="0" data-i18n data-i18n-aria-label="__MSG_dismissIconDescription__"></span>
</div>
</div>
Expand Down
Loading