Bug 1377439 - Should adapt the oboarding UI to the hight-contrast display mode, r=mossop draft
authorFischer.json <fischer.json@gmail.com>
Tue, 18 Jul 2017 14:26:25 +0800
changeset 610320 008295ce6f7b5f758a65be6953ed1c1efe00dffd
parent 610234 5e73b9798464c3f7106f0161dc9a49b234f42f9c
child 637830 592e3502d33cdfb5e618c40dd7ab00b651c16b1d
push id68859
push userbmo:fliu@mozilla.com
push dateTue, 18 Jul 2017 07:26:40 +0000
reviewersmossop
bugs1377439
milestone56.0a1
Bug 1377439 - Should adapt the oboarding UI to the hight-contrast display mode, r=mossop This patch makes the mininmum and the important elements and images visible in the high-constrast mode, including - making button have border (using the given default grey border in the high-constrast mode) - making the upper-left overlay button's fox image visible - making the images of the X close buttons on the notification bar and on the overlay visible - making that the navigation item of the current tour on the overlay's right side would have outline - making the hovered navigation item on the overlay's right side would have outline - making sure using <button> element for buttons for a better semantic - changing #onboarding-overlay-icon to #onboarding-overlay-button for a better semantic MozReview-Commit-ID: Aj6wndb4to9
browser/extensions/onboarding/content/onboarding.css
browser/extensions/onboarding/content/onboarding.js
browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -18,54 +18,64 @@
   background: rgb(54, 57, 89, 0.8); /* #363959, 0.8 opacity */
   display: none;
 }
 
 #onboarding-overlay.onboarding-opened {
   display: block;
 }
 
-#onboarding-overlay-icon {
-  width: 36px;
-  height: 29px;
+#onboarding-overlay-button {
   position: absolute;
   cursor: pointer;
   top: 30px;
   offset-inline-start: 30px;
-  background: url("img/overlay-icon.svg") no-repeat;
+  border: none;
+  /* Set to none so no grey contrast background in the high-contrast mode */
+  background: none;
+}
+
+#onboarding-overlay-button-icon {
+  width: 36px;
 }
 
 #onboarding-overlay-dialog,
 .onboarding-hidden {
   display: none;
 }
 
-#onboarding-overlay-close-btn,
-#onboarding-notification-close-btn {
-  position: absolute;
-  top: 15px;
-  offset-inline-end: 15px;
-  cursor: pointer;
-  width: 16px;
-  height: 16px;
-  background-image: url(chrome://browser/skin/sidebar/close.svg);
-  background-position: center center;
-  background-repeat: no-repeat;
-  padding: 12px;
+.onboarding-close-btn {
+   position: absolute;
+   top: 15px;
+   offset-inline-end: 15px;
+   cursor: pointer;
+   width: 16px;
+   height: 16px;
+   padding: 12px;
+   border: none;
+   background: var(--onboarding-overlay-dialog-background-color);
+ }
+
+.onboarding-close-btn::before {
+  content: url(chrome://browser/skin/sidebar/close.svg);
+  display: block;
+  margin-top: -8px;
+  margin-inline-start: -8px;
 }
 
-#onboarding-overlay-close-btn:hover,
+.onboarding-close-btn:hover,
 #onboarding-notification-close-btn:hover {
   background-color: rgba(204, 204, 204, 0.6);
 }
 
 #onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
+  --onboarding-overlay-dialog-background-color: #f5f5f7;
   width: 960px;
   height: 510px;
-  background: #f5f5f7;
+  background: var(--onboarding-overlay-dialog-background-color);
   border: 1px solid rgba(9, 6, 13, 0.1); /* #09060D, 0.1 opacity */
   border-radius: 3px;
   position: relative;
   margin: 0 calc(50% - 480px);
   top: calc(50% - 255px);
   display: grid;
   grid-template-rows: [dialog-start] 70px [page-start] 1fr [footer-start] 30px [dialog-end];
   grid-template-columns: [dialog-start] 230px [page-start] 1fr [dialog-end];
@@ -111,20 +121,23 @@
 
 /* Onboarding tour list */
 #onboarding-tour-list {
   margin: 40px 0 0 0;
   padding: 0;
 }
 
 #onboarding-tour-list > li {
+  --padding-inline-start: 49px;
+  --padding-top: 14px;
+  --padding-bottom: 14px;
   list-style: none;
-  padding-inline-start: 49px;
-  padding-top: 14px;
-  padding-bottom: 14px;
+  padding-inline-start: var(--padding-inline-start);
+  padding-top: var(--padding-top);
+  padding-bottom: var(--padding-bottom);
   margin-inline-start: 16px;
   margin-bottom: 9px;
   background-repeat: no-repeat;
   background-position: left 17px top 14px;
   background-size: 20px;
   font-size: 16px;
   cursor: pointer;
 }
@@ -137,22 +150,29 @@
   content: url("img/icons_tour-complete.svg");
   position: relative;
   offset-inline-start: 3px;
   top: -10px;
   float: inline-start;
 }
 
 #onboarding-tour-list > li.onboarding-complete {
-  padding-inline-start: 29px;
+  --padding-inline-start: 29px;
 }
 
 #onboarding-tour-list > li.onboarding-active,
 #onboarding-tour-list > li:hover {
   color: #0A84FF;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
+  /* Substract 1px for the 1px transparent or a 1px shift would happen */
+  padding-inline-start: calc(var(--padding-inline-start) - 1px);
+  padding-top: calc(var(--padding-top) - 1px);
+  padding-bottom: calc(var(--padding-bottom) - 1px);
+  background-color: #fff;
 }
 
 /* Default browser tour */
 #onboarding-tour-is-default-browser-msg {
   font-size: 16px;
   line-height: 21px;
   float: inline-end;
   margin-inline-end: 26px;
@@ -259,17 +279,18 @@
 }
 
 .onboarding-tour-action-button {
   padding: 10px 20px;
   font-size: 15px;
   font-weight: 600;
   line-height: 21px;
   background: #0a84ff;
-  border: none;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
   border-radius: 0;
   color: #fff;
   float: inline-end;
   margin-inline-end: 26px;
   margin-top: -32px;
 }
 
 .onboarding-tour-action-button:hover:not([disabled]) ,
@@ -345,24 +366,25 @@
 #onboarding-tour-sync.onboarding-active,
 #onboarding-tour-sync:hover,
 #onboarding-notification-bar[data-target-tour-id=onboarding-tour-sync] #onboarding-notification-tour-icon {
   background-image: url("img/icons_sync-colored.svg");
 }
 
 /* Tour Notifications */
 #onboarding-notification-bar {
+  --onboarding-notification-bar-background-color: rgba(255, 255, 255, 0.97);
   position: fixed;
   z-index: 20998; /* We want this always under #onboarding-overlay */
   left: 0;
   bottom: 0;
   width: 100%;
   height: 122px;
   min-width: 640px;
-  background: rgba(255, 255, 255, 0.97);
+  background: var(--onboarding-notification-bar-background-color);
   border-top: 2px solid #e9e9e9;
   transition: transform 0.8s;
   transform: translateY(122px);
 }
 
 #onboarding-notification-bar.onboarding-opened {
   transition: none;
   transform: translateY(0px);
@@ -397,18 +419,17 @@
   max-width: 90px;
   border-radius: calc(var(--height) / 2);
   border: 1px solid #fff;
   padding: var(--vpadding) 10px;
   text-align: center;
 }
 
 #onboarding-notification-close-btn {
-  background-color: rgba(255, 255, 255, 0.97);
-  border: none;
+  background: var(--onboarding-notification-bar-background-color);
   position: absolute;
   offset-block-start: 50%;
   offset-inline-end: 34px;
   transform: translateY(-50%);
 }
 
 #onboarding-notification-message-section {
   height: 100%;
@@ -443,17 +464,18 @@
   min-width: 64px;
   height: 64px;
   background-size: 64px;
   background-repeat: no-repeat;
 }
 
 #onboarding-notification-action-btn {
   background: #0a84ff;
-  border: none;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
   border-radius: 0;
   padding: 10px 20px;
   font-size: 14px;
   color: #fff;
 }
 
 @media all and (max-width: 960px) {
   #onboarding-notification-icon {
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -279,17 +279,17 @@ class Onboarding {
   _initUI() {
     if (this.uiInitialized) {
       return;
     }
     this.uiInitialized = true;
     this._tourItems = [];
     this._tourPages = [];
 
-    this._overlayIcon = this._renderOverlayIcon();
+    this._overlayIcon = this._renderOverlayButton();
     this._overlayIcon.addEventListener("click", this);
     this._window.document.body.appendChild(this._overlayIcon);
 
     this._overlay = this._renderOverlay();
     this._overlay.addEventListener("click", this);
     this._window.document.body.appendChild(this._overlay);
 
     this._loadJS(TOUR_AGENT_JS_URI);
@@ -368,17 +368,17 @@ class Onboarding {
       this._window.cancelIdleCallback(this._resizeTimerId);
       this._resizeTimerId =
         this._window.requestIdleCallback(() => this._resizeUI());
 
       return;
     }
 
     switch (evt.target.id) {
-      case "onboarding-overlay-icon":
+      case "onboarding-overlay-button":
       case "onboarding-overlay-close-btn":
       // If the clicking target is directly on the outer-most overlay,
       // that means clicking outside the tour content area.
       // Let's toggle the overlay.
       case "onboarding-overlay":
         this.toggleOverlay();
         break;
       case "onboarding-notification-close-btn":
@@ -647,17 +647,17 @@ class Onboarding {
       <section id="onboarding-notification-message-section">
         <div id="onboarding-notification-tour-icon"></div>
         <div id="onboarding-notification-body">
           <h6 id="onboarding-notification-tour-title"></h6>
           <span id="onboarding-notification-tour-message"></span>
         </div>
         <button id="onboarding-notification-action-btn"></button>
       </section>
-      <button id="onboarding-notification-close-btn"></button>
+      <button id="onboarding-notification-close-btn" class="onboarding-close-btn"></button>
     `;
     let toolTip = this._bundle.formatStringFromName("onboarding.notification-icon-tool-tip", [BRAND_SHORT_NAME], 1);
     div.querySelector("#onboarding-notification-icon").setAttribute("data-tooltip", toolTip);
     return div;
   }
 
   hide() {
     this.setToursCompleted(this._tours.map(tour => tour.id));
@@ -675,17 +675,17 @@ class Onboarding {
 
   _renderOverlay() {
     let div = this._window.document.createElement("div");
     div.id = "onboarding-overlay";
     // We use `innerHTML` for more friendly reading.
     // The security should be fine because this is not from an external input.
     div.innerHTML = `
       <div id="onboarding-overlay-dialog">
-        <span id="onboarding-overlay-close-btn"></span>
+        <button id="onboarding-overlay-close-btn" class="onboarding-close-btn"></button>
         <header id="onboarding-header"></header>
         <nav>
           <ul id="onboarding-tour-list"></ul>
         </nav>
         <footer id="onboarding-footer">
           <input type="checkbox" id="onboarding-tour-hidden-checkbox" /><label for="onboarding-tour-hidden-checkbox"></label>
         </footer>
       </div>
@@ -693,20 +693,24 @@ class Onboarding {
 
     div.querySelector("label[for='onboarding-tour-hidden-checkbox']").textContent =
        this._bundle.GetStringFromName("onboarding.hidden-checkbox-label-text");
     div.querySelector("#onboarding-header").textContent =
        this._bundle.formatStringFromName("onboarding.overlay-title", [BRAND_SHORT_NAME], 1);
     return div;
   }
 
-  _renderOverlayIcon() {
-    let img = this._window.document.createElement("div");
-    img.id = "onboarding-overlay-icon";
-    return img;
+  _renderOverlayButton() {
+    let button = this._window.document.createElement("button");
+    button.id = "onboarding-overlay-button";
+    let img = this._window.document.createElement("img");
+    img.id = "onboarding-overlay-button-icon";
+    img.src = "resource://onboarding/img/overlay-icon.svg";
+    button.appendChild(img);
+    return button;
   }
 
   _loadTours(tours) {
     let itemsFrag = this._window.document.createDocumentFragment();
     let pagesFrag = this._window.document.createDocumentFragment();
     for (let tour of tours) {
       // Create tour navigation items dynamically
       let li = this._window.document.createElement("li");
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
  "use strict";
 
 function assertOnboardingDestroyed(browser) {
   return ContentTask.spawn(browser, {}, function() {
     let expectedRemovals = [
       "#onboarding-overlay",
-      "#onboarding-overlay-icon"
+      "#onboarding-overlay-button"
     ];
     for (let selector of expectedRemovals) {
       let removal = content.document.querySelector(selector);
       ok(!removal, `Should remove ${selector} onboarding element`);
     }
   });
 }
 
@@ -31,17 +31,17 @@ add_task(async function test_hide_onboar
   resetOnboardingDefaultState();
 
   let tourIds = TOUR_IDs;
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let expectedPrefUpdates = [
     promisePrefUpdated("browser.onboarding.hidden", true),
     promisePrefUpdated("browser.onboarding.notification.finished", true)
   ];
@@ -62,17 +62,17 @@ add_task(async function test_click_actio
   resetOnboardingDefaultState();
 
   let tourIds = TOUR_IDs;
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let completedTourId = tourIds[0];
   let expectedPrefUpdate = promisePrefUpdated(`browser.onboarding.tour.${completedTourId}.completed`, true);
   await BrowserTestUtils.synthesizeMouseAtCenter(`#${completedTourId}-page .onboarding-tour-action-button`, {}, gBrowser.selectedBrowser);
   await expectedPrefUpdate;
@@ -96,17 +96,17 @@ add_task(async function test_set_right_t
     setTourCompletedState(tourIds[i], i % 2 == 0);
   }
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   for (let i = tabs.length - 1; i >= 0; --i) {
     let tab = tabs[i];
     for (let j = 0; j < tourIds.length; ++j) {
       await assertTourCompletedStyle(tourIds[j], j % 2 == 0, tab.linkedBrowser);
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
@@ -5,17 +5,17 @@
 
 add_task(async function test_onboarding_default_new_tourset() {
   resetOnboardingDefaultState();
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, TOUR_IDs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {
@@ -43,17 +43,17 @@ add_task(async function test_onboarding_
     ["browser.onboarding.newtour", "private,addons,customize"],
   ]});
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, CUSTOM_NEW_TOURs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {
@@ -80,17 +80,17 @@ add_task(async function test_onboarding_
     ["browser.onboarding.updatetour", "customize,private,addons"],
   ]});
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, CUSTOM_UPDATE_TOURs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {