diff --git a/devtools/client/locales/en-US/accessibility.properties b/devtools/client/locales/en-US/accessibility.properties index f8d35979ddc7..5fe943773ae6 100644 --- a/devtools/client/locales/en-US/accessibility.properties +++ b/devtools/client/locales/en-US/accessibility.properties @@ -152,6 +152,10 @@ accessibility.contrast.annotation.AAA=Meets WCAG AAA standards for accessible te # run time with the accessibility.learnMore string. accessibility.contrast.annotation.fail=Does not meet WCAG standards for accessible text. %S +# LOCALIZATION NOTE (accessibility.contrast.annotation.transparent.error): A title text for the +# paragraph suggesting a fix for error in color contrast calculation for text nodes with zero alpha. +accessibility.contrast.annotation.transparent.error=Pick a color that is not transparent. + # LOCALIZATION NOTE (accessibility.badges): A title text for the group of badges # that are rendered for each accessible row within the accessibility tree when # one or more accessibility checks fail. diff --git a/devtools/client/locales/en-US/inspector.properties b/devtools/client/locales/en-US/inspector.properties index d44a3a7d928c..219e0cd92b9e 100644 --- a/devtools/client/locales/en-US/inspector.properties +++ b/devtools/client/locales/en-US/inspector.properties @@ -511,3 +511,9 @@ markupView.scrollableBadge.tooltip=This element has scrollable overflow. # LOCALIZATION NOTE (rulePreviewTooltip.noAssociatedRule): This is the text displayed inside # the RulePreviewTooltip when a rule cannot be found for a CSS property declaration. rulePreviewTooltip.noAssociatedRule=No associated rule + +# LOCALIZATION NOTE (colorPickerTooltip.contrastAgainstBgTitle): A title text for the +# contrast ratio value description that labels the background the color contrast ratio is calculated +# against, used together with the actual background color. %S in the content will be replaced by a +# span (containing bg color swatch) and textNode (containing bg color hex string) at run time +colorPickerTooltip.contrastAgainstBgTitle=Calculated against background: %S diff --git a/devtools/client/shared/widgets/Spectrum.js b/devtools/client/shared/widgets/Spectrum.js index beee2942bd9a..bcd4e3384dca 100644 --- a/devtools/client/shared/widgets/Spectrum.js +++ b/devtools/client/shared/widgets/Spectrum.js @@ -8,9 +8,11 @@ const EventEmitter = require("devtools/shared/event-emitter"); const { MultiLocalizationHelper } = require("devtools/shared/l10n"); const L10N = new MultiLocalizationHelper( "devtools/shared/locales/en-US/accessibility.properties", - "devtools/client/locales/en-US/accessibility.properties" + "devtools/client/locales/en-US/accessibility.properties", + "devtools/client/locales/en-US/inspector.properties" ); const XHTML_NS = "http://www.w3.org/1999/xhtml"; +const COLOR_HEX_WHITE = "#ffffff"; loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true); loader.lazyRequireGetter( @@ -148,6 +150,13 @@ function Spectrum(parentEl, rgb) { ".accessibility-contrast-value" ); + // Create the learn more info button + const learnMore = this.document.createElementNS(XHTML_NS, "button"); + learnMore.id = "learn-more-button"; + learnMore.className = "learn-more"; + learnMore.title = L10N.getStr("accessibility.learnMore"); + this.spectrumContrast.appendChild(learnMore); + if (rgb) { this.rgb = rgb; this.updateUI(); @@ -495,9 +504,8 @@ Spectrum.prototype = { * Contrast ratio components include: * - contrastLargeTextIndicator: Hidden by default, shown when text has large font * size if there is no error in calculation. - * - contrastValue: Set to calculated value and score if no - * error. Set to error text if there is an error in - * calculation. + * - contrastValue: Set to calculated value and score. Set to error text + * if there is an error in calculation. */ updateContrast: function() { // Remove additional classes on spectrum contrast, leaving behind only base classes @@ -522,12 +530,22 @@ Spectrum.prototype = { this.contrastValue.textContent = L10N.getStr( "accessibility.contrast.error" ); + this.contrastValue.title = L10N.getStr( + "accessibility.contrast.annotation.transparent.error" + ); this.spectrumContrast.classList.remove("large-text"); return; } this.contrastValue.classList.toggle(score, true); this.contrastValue.textContent = value.toFixed(2); + this.contrastValue.title = L10N.getFormatStr( + `accessibility.contrast.annotation.${score}`, + L10N.getFormatStr( + "colorPickerTooltip.contrastAgainstBgTitle", + COLOR_HEX_WHITE + ) + ); this.spectrumContrast.classList.toggle("large-text", isLargeText); }, diff --git a/devtools/client/shared/widgets/spectrum.css b/devtools/client/shared/widgets/spectrum.css index f9217d75e5ca..9d54dbf420b9 100644 --- a/devtools/client/shared/widgets/spectrum.css +++ b/devtools/client/shared/widgets/spectrum.css @@ -2,6 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + :root { + --learn-more-underline: var(--grey-30); +} + +.theme-dark:root { + --learn-more-underline: var(--grey-50); +} + #eyedropper-button { margin-inline-end: 5px; display: block; @@ -177,6 +185,8 @@ http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */ .spectrum-color-contrast { padding-block-start: 8px; padding-inline-start: 3px; + align-items: stretch; + line-height: 1.2em; } .spectrum-color-contrast.visible.large-text .accessibility-color-contrast-large-text { @@ -204,7 +214,7 @@ http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */ .spectrum-color-contrast .accessibility-contrast-value { color: var(--theme-body-color); - line-height: 1em; + border-bottom: 1px solid var(--learn-more-underline); } .spectrum-color-contrast .accessibility-contrast-value:not(:empty)::before { @@ -212,3 +222,21 @@ http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */ content: none; padding-inline-start: 2px; } + +.learn-more { + background-size: auto; + background-repeat: no-repeat; + -moz-context-properties: fill; + background-image: url(chrome://devtools/skin/images/info-small.svg); + background-color: transparent; + fill: var(--theme-icon-dimmed-color); + border: none; + margin-left: auto; + margin-top: 2px; + margin-right: 1px; +} + +.learn-more:hover { + fill: var(--theme-icon-color); + cursor: pointer; +} diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js index 0b171749e740..fc15b9763cd8 100644 --- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js +++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js @@ -11,6 +11,10 @@ const { LocalizationHelper } = require("devtools/shared/l10n"); const L10N = new LocalizationHelper( "devtools/client/locales/inspector.properties" ); +const { openDocLink } = require("devtools/client/shared/link"); +const { + A11Y_CONTRAST_LEARN_MORE_LINK, +} = require("devtools/client/accessibility/constants"); const TELEMETRY_PICKER_EYEDROPPER_OPEN_COUNT = "DEVTOOLS_PICKER_EYEDROPPER_OPENED_COUNT"; @@ -44,6 +48,7 @@ class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip { this.spectrum = this.setColorPickerContent([0, 0, 0, 1]); this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this); this._openEyeDropper = this._openEyeDropper.bind(this); + this._openDocLink = this._openDocLink.bind(this); this.cssColor4 = supportsCssColor4ColorFunction(); } @@ -133,6 +138,13 @@ class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip { eyeButton.title = L10N.getStr("eyedropper.disabled.title"); } + const learnMoreButton = this.tooltip.container.querySelector( + "#learn-more-button" + ); + if (learnMoreButton) { + learnMoreButton.addEventListener("click", this._openDocLink); + } + // After spectrum properties are set, update the tooltip content size. // If contrast is enabled, the tooltip will have additional contrast content // and tooltip size needs to be updated to account for it. @@ -206,6 +218,11 @@ class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip { }); } + _openDocLink() { + openDocLink(A11Y_CONTRAST_LEARN_MORE_LINK); + this.hide(); + } + _onEyeDropperDone() { this.eyedropperOpen = false; this.activeSwatch = null;