-
Notifications
You must be signed in to change notification settings - Fork 128
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
Conversation
Great PR! From a first look at the code it looks good. And it seems you also fixed #83 (you may add it to the PR description here, so it is automatically closed). As for the error message: good point to show an error if the color is exactly the same. However, you may show it even earlier when it is not at all readable by a qr code scanner at all even if some text would still be visible to the human eye with that contrast. So could you maybe do some tests with some qr code scanners to see at which contrast ratio a qr code can still be scanned. (Or maybe you find an official document/spec, which suggests a minimum contrast) So the levels should maybe be like:
BTW, if you want, you can make use of the new "action buttons" I've implemented for messages. Maybe reset the last changed color? Or automatically calculate a good color, like the exact contrast color? That would make the error messages even more helpful. Remember there is also #20, which is very similar and may be accomplished with the same code or just a minor change, or so. |
src/common/common.js
Outdated
@@ -732,7 +732,7 @@ const MessageHandler = (function () {// eslint-disable-line no-unused-vars | |||
if (typeof args[0] === "boolean") { | |||
isDismissable = args.shift(); | |||
} | |||
if (typeof args[0] !== undefined && args[0].text !== undefined && args[0].action !== undefined) { | |||
if (typeof args[0] !== "undefined" && args[0].text !== "undefined" && args[0].action !== "undefined") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In ES6 it is no longer needed to compare the typeof value to the string of undefined
explicitly. See https://stackoverflow.com/questions/34596489/es2015-2016-way-of-typeof-varname-undefined#34596516
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmh... But it gave me an error and when I printed typeof args[0] !== undefined
it returned true
even if the array was empty. And typeof args[0] !== "undefined"
returned false
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And and yeah… it's wrong. As https://stackoverflow.com/questions/34596489/es2015-2016-way-of-typeof-varname-undefined#34596516 says, we may better not use that "typeof" for it anyway. In ES6 one can just check whether it is undefined directly.
So better not use typeof
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. I will change that.
src/common/common.js
Outdated
@@ -770,7 +770,7 @@ const MessageHandler = (function () {// eslint-disable-line no-unused-vars | |||
|
|||
elActionButton.textContent = browser.i18n.getMessage(actionButton.text) || actionButton.text; | |||
elActionButton.classList.remove("invisible"); | |||
} else { | |||
} else if (elActionButton) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good point!
Dismiss icon (https://github.com/rugk/offline-qr-code/pull/86/files#diff-ccdc4da75e7ac1061a8cc0851e3c7122R751) needs the same adjustments. (but I can also do them by myself)
I just tested it with Zxing and it scanned even codes with a contrast ratio of below 2 (1 is the lowest possible value). The only problem were inverted codes (which could not be scanned at all below a contrast ratio of 5) but that should be covered with #20... I will do now some more tests with other applications. |
Indeed, also Zxing ("Barcode Reader") is one of the few apps, that actually support scanning inverted QR codes. You have to enable it in the options. |
src/options.js
Outdated
* @function | ||
* @private | ||
* @param {string} hex | ||
* @returns {Array} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function can also return null
as it seems…
src/options.js
Outdated
*/ | ||
function hexToRgb(hex) { | ||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); | ||
return result ? [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh, splitting the conditional operator on multiple lines? Better use a traditional if
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So LGTM from a code review POV.
Other stuff I said before still outstanding.
I found no official document. All I found just said that the contrast should not be too low 😄
Do you mean the opposite color of the QR code (or the background)? |
BTW this color stuff get's much if you want to also implement support for opposite color there. So maybe you could split that color stuff (only that color caluclation/etc.) into it's own module. (Could be in commons.js or in options.js – does not really matter, as we currently won't use it anywhere else than in the options, but it will be restructured into |
src/common/common.js
Outdated
@@ -732,7 +732,7 @@ const MessageHandler = (function () {// eslint-disable-line no-unused-vars | |||
if (typeof args[0] === "boolean") { | |||
isDismissable = args.shift(); | |||
} | |||
if (typeof args[0] !== undefined && args[0].text !== undefined && args[0].action !== undefined) { | |||
if (args[0] !== undefined && args[0].text !== "undefined" && args[0].action !== "undefined") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW I cherry-picked this change into the master as it is just a bug fix for a issue I introduced. But BTW the last undefines do not need (or maybe even should not) be quoted. (typeof is not even used there).
Basically I suggest you to merge the master into my branch, that should fix these conflicts.
It was a little bit more work than I thought but now there is a small button next to each message which (when clicked) calculates the complementary color of the QR code and sets the calculated color as the new background of the code automatically.
I just donwloaded 3 different QR code scanner apps and tried some random settings while seeing the contrast ratio value in the console. I think your tests were more extensive... |
1. red errors need a white text on the action button 2. if there is few space for the action button, it should not overflow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/options.js
Outdated
} else { | ||
MessageHandler.hideInfo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this could get problematic, because we also show a permanent "managed setting" info message when this is used (see OptionHandler.showManagedInfo
).
What we could do about this:
- check
managedInfoIsShown
and do not show or hide the info message 8easy, but not really nice) - use a custom element for that managed info thing – this is an information, which stays there, anyway. (the way to go, IMHO)
- implement Extend MessageHandler for showing multiple messages #9 (too hard and not needed for this, IMHO)
So I can do the second version after your PR is merged.
src/options.js
Outdated
browser.storage.sync.set({ | ||
"qrBackgroundColor": invertedColor | ||
}).catch((error) => { | ||
Logger.logError("could not save option", option, ": ", error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great you added this button! As for this line just one comment: As the logger passes this to console
anyway, I think you do not need to add a space after the :
. All values are separated automatically (with a space as it seems) by the browser.
@@ -1372,6 +1372,104 @@ const RandomTips = (function () {// eslint-disable-line no-unused-vars | |||
return me; | |||
})(); | |||
|
|||
const Colors = (() => { // eslint-disable-line no-unused-vars |
There was a problem hiding this comment.
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.
src/_locales/en/messages.json
Outdated
"description": "The message shown when the color for the QR code and the background are equal" | ||
}, | ||
"messageAutoSelectColorButton": { | ||
"message": "Calculate best contrast", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MAybe you can try to shorten this a bit?
Instead of calculate maybe just "Use". (That the JS has to calculate this is a technical detail the user does not even want to know. 😄)
And instead of contrast maybe contrasting color? Or something else like "best color"? Remember it does not matter to be technically 100% accurate here, the user should just know what happens when they click the button.
Theoretically it could e.g. also be okay for the user to get some color variation (i.e. not 100%ly the contrast color, but a color, which has enough contrast, so the message goes away.)
} | ||
}; | ||
|
||
// breakpoints: https://github.com/rugk/offline-qr-code/pull/86#issuecomment-390426286 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and once we are at it. You can link to my comment, yes, but please still add comments for the colors, i.e. the accessibility guidelines I mentioned.
Or even better remember the "no magic numbers" rules. So put these numbers into constants. As they are color stuff not (only) related to the options handler, the best would be to use these as constants in the Colors
module, i.e. make them "public constants". (add to me
in that module)
src/_locales/de/messages.json
Outdated
"desription": "The message shown when the contrast ratio is too low" | ||
}, | ||
"lowContrastRatioWarning": { | ||
"message": "Das Kontrastverhältnis zwischen der Farbe des QR-Codes und dem Hintergrund ist wahrscheinlich zu niedrig um von den meisten QR-Code-Scannern erkannt zu werden", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shorter:
das Kontrastverhältnis -> der Kontrast
is "wahrscheinlich" neccessary? Maybe just use subjunctive ("Konjunktiv") here, too (as in the English version).
grammar:
dem Hintergrund -> dessen Hintergrund
In general you may reword it. Keep the fact that it may not be scanned by many QR code readers.
src/options.js
Outdated
MessageHandler.showWarning("lowContrastRatioWarning", false, actionButton); | ||
} else if (colorContrast <= 4.5) { | ||
// show only an info when the contrast is low but most of the scanners can still read it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I rather thought about "WCAG 2.0 level AA" (so please add that), but this information is also good.
Still prefer them as constants though. 😄
Really? How does that work? I can't find the issue in the code currently 😄... |
src/common/common.js
Outdated
const me = {}; | ||
|
||
me.ContrastBreakpoints = { | ||
A: 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No my numbers were not all the A/AA/AAA things…
But I'll change that by myself.
Small usage change: now uses 3.1 instead 3 for warning, but that should hardly matter.
You just change the colors again and again and thus "fix" them. When one message is shown, it does not go away unless you got no message at all… 😄 BTW I changed the constant there. And I could not find any contrast ratio definition for WCAG level A, I guess they start at level AA for contrasts. |
Any ETA on this? |
I think with the latest commit I fixed the issue where multiple messages where shown. Is there still something to do? I could not find anything... |
No, all right. As for an explicit message box for only these messages, I'll add this later, as said. |
Merged in 313a5f6 |
In rugk@313a5f6 the PR rugk#86 was not really merged in a good way.
This PR fixes #19
Edit: it also fixes #83
I'm not sure what should be the best contrast ratio (currently it is 4 because the tests I've done with https://dequeuniversity.com/rules/axe/2.2/color-contrast returned that 4 is OK if the text is not too small...)
The amount can be changed here:
https://github.com/ENT8R/offline-qr-code/blob/819d68d403137df93eda018b10553e35e1e010fd/src/options.js#L217