Bug 1564968 - add highlighting support for keyboard issues. r=rcaliman
authorYura Zenevich <yura.zenevich@gmail.com>
Wed, 28 Aug 2019 13:09:23 +0000
changeset 554202 bcca86af8ab52be4d955431afe0822d2fbe2c0ef
parent 554201 e9fa44e62b03c82e363f1d710b3efeb56004fe9d
child 554203 083ba02b90d27293a4b127de41f680089f63d15a
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcaliman
bugs1564968
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1564968 - add highlighting support for keyboard issues. r=rcaliman Differential Revision: https://phabricator.services.mozilla.com/D43443
devtools/client/accessibility/accessibility.css
devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
devtools/client/accessibility/test/jest/components/__snapshots__/text-label-check.test.js.snap
devtools/client/accessibility/test/jest/components/audit-filter.test.js
devtools/client/accessibility/test/jest/components/badges.test.js
devtools/client/accessibility/test/jest/components/contrast-badge.test.js
devtools/client/accessibility/test/jest/components/text-label-check.test.js
devtools/client/accessibility/test/mochitest/contrast.snapshots.js
devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
devtools/client/locales/en-US/accessibility.properties
devtools/client/themes/accessibility-color-contrast.css
devtools/server/actors/highlighters.css
devtools/server/actors/highlighters/utils/accessibility.js
devtools/server/actors/highlighters/xul-accessible.js
devtools/server/tests/browser/browser.ini
devtools/server/tests/browser/browser_accessibility_infobar_audit_keyboard.js
devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
devtools/server/tests/browser/browser_accessibility_node_audit.js
devtools/server/tests/browser/browser_accessibility_walker_audit.js
devtools/shared/constants.js
devtools/shared/locales/en-US/accessibility.properties
--- a/devtools/client/accessibility/accessibility.css
+++ b/devtools/client/accessibility/accessibility.css
@@ -718,17 +718,17 @@ body {
 .accessibility-text-label-check .icon {
   display: inline;
   -moz-context-properties: fill;
   vertical-align: top;
   margin-block-start: 2px;
   margin-inline-end: 4px;
 }
 
-.accessibility-text-label-check .icon.fail {
+.accessibility-text-label-check .icon.FAIL {
   fill: var(--theme-icon-error-color);
 }
 
 .accessibility-text-label-check .icon.WARNING {
   fill: var(--theme-icon-warning-color);
 }
 
 .accessibility-check,
--- a/devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_sidebar_checks.js
@@ -1,13 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+const {
+  accessibility: { SCORES },
+} = require("devtools/shared/constants");
+
 const TEST_URI = `<html>
   <head>
     <meta charset="utf-8"/>
     <title>Accessibility Panel Test</title>
   </head>
   <body>
     <p style="color: red;">Red</p>
     <p style="color: blue;">Blue</p>
@@ -40,17 +44,17 @@ const tests = [
     },
     expected: {
       audit: {
         CONTRAST: {
           value: 4.0,
           color: [255, 0, 0, 1],
           backgroundColor: [255, 255, 255, 1],
           isLargeText: false,
-          score: "fail",
+          score: SCORES.FAIL,
         },
       },
     },
   },
   {
     desc: "Check accessible representing text node in blue.",
     setup: async ({ doc }) => {
       await toggleRow(doc, 3);
@@ -58,17 +62,17 @@ const tests = [
     },
     expected: {
       audit: {
         CONTRAST: {
           value: 8.59,
           color: [0, 0, 255, 1],
           backgroundColor: [255, 255, 255, 1],
           isLargeText: false,
-          score: "AAA",
+          score: SCORES.AAA,
         },
       },
     },
   },
 ];
 
 /**
  * Test that checks the Accessibility panel sidebar.
--- a/devtools/client/accessibility/test/jest/components/__snapshots__/text-label-check.test.js.snap
+++ b/devtools/client/accessibility/test/jest/components/__snapshots__/text-label-check.test.js.snap
@@ -1,7 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`TextLabelCheck component: BEST_PRACTICES render 1`] = `"<div role=\\"presentation\\" class=\\"accessibility-check\\"><h3 class=\\"accessibility-check-header\\"></h3><div role=\\"presentation\\" class=\\"accessibility-text-label-check\\"><img src=\\"chrome://devtools/skin/images/info.svg\\" class=\\"icon BEST_PRACTICES\\"><p class=\\"accessibility-check-annotation\\"></p></div></div>"`;
 
-exports[`TextLabelCheck component: WARNING render 1`] = `"<div role=\\"presentation\\" class=\\"accessibility-check\\"><h3 class=\\"accessibility-check-header\\"></h3><div role=\\"presentation\\" class=\\"accessibility-text-label-check\\"><img src=\\"chrome://devtools/skin/images/alert.svg\\" class=\\"icon WARNING\\"><p class=\\"accessibility-check-annotation\\"></p></div></div>"`;
+exports[`TextLabelCheck component: FAIL render 1`] = `"<div role=\\"presentation\\" class=\\"accessibility-check\\"><h3 class=\\"accessibility-check-header\\"></h3><div role=\\"presentation\\" class=\\"accessibility-text-label-check\\"><img src=\\"chrome://devtools/skin/images/error.svg\\" class=\\"icon FAIL\\"><p class=\\"accessibility-check-annotation\\"></p></div></div>"`;
 
-exports[`TextLabelCheck component: fail render 1`] = `"<div role=\\"presentation\\" class=\\"accessibility-check\\"><h3 class=\\"accessibility-check-header\\"></h3><div role=\\"presentation\\" class=\\"accessibility-text-label-check\\"><img src=\\"chrome://devtools/skin/images/error.svg\\" class=\\"icon fail\\"><p class=\\"accessibility-check-annotation\\"></p></div></div>"`;
+exports[`TextLabelCheck component: WARNING render 1`] = `"<div role=\\"presentation\\" class=\\"accessibility-check\\"><h3 class=\\"accessibility-check-header\\"></h3><div role=\\"presentation\\" class=\\"accessibility-text-label-check\\"><img src=\\"chrome://devtools/skin/images/alert.svg\\" class=\\"icon WARNING\\"><p class=\\"accessibility-check-annotation\\"></p></div></div>"`;
--- a/devtools/client/accessibility/test/jest/components/audit-filter.test.js
+++ b/devtools/client/accessibility/test/jest/components/audit-filter.test.js
@@ -14,16 +14,20 @@ const Provider = createFactory(
 const ConnectedAuditFilterClass = require("devtools/client/accessibility/components/AuditFilter");
 const AuditFilterClass = ConnectedAuditFilterClass.WrappedComponent;
 const AuditFilter = createFactory(ConnectedAuditFilterClass);
 const {
   setupStore,
 } = require("devtools/client/accessibility/test/jest/helpers");
 const { FILTERS } = require("devtools/client/accessibility/constants");
 
+const {
+  accessibility: { SCORES },
+} = require("devtools/shared/constants");
+
 describe("AuditController component:", () => {
   it("audit filter not filtered", () => {
     const store = setupStore();
 
     const wrapper = mount(Provider({ store }, AuditFilter({}, span())));
     expect(wrapper.html()).toMatchSnapshot();
 
     const filter = wrapper.find(AuditFilterClass);
@@ -62,17 +66,17 @@ describe("AuditController component:", (
         AuditFilter(
           {
             checks: {
               CONTRAST: {
                 value: 5.11,
                 color: [255, 0, 0, 1],
                 backgroundColor: [255, 255, 255, 1],
                 isLargeText: false,
-                score: "AA",
+                score: SCORES.AA,
               },
             },
           },
           span()
         )
       )
     );
     expect(wrapper.html()).toMatchSnapshot();
@@ -84,17 +88,17 @@ describe("AuditController component:", (
       preloadedState: { audit: { filters: { [FILTERS.CONTRAST]: true } } },
     });
 
     const CONTRAST = {
       value: 3.1,
       color: [255, 0, 0, 1],
       backgroundColor: [255, 255, 255, 1],
       isLargeText: false,
-      score: "fail",
+      score: SCORES.FAIL,
     };
 
     const wrapper = mount(
       Provider(
         { store },
         AuditFilter(
           {
             checks: { CONTRAST },
@@ -116,17 +120,17 @@ describe("AuditController component:", (
 
     const CONTRAST = {
       min: 1.19,
       max: 1.39,
       color: [128, 128, 128, 1],
       backgroundColorMin: [219, 106, 116, 1],
       backgroundColorMax: [156, 145, 211, 1],
       isLargeText: false,
-      score: "fail",
+      score: SCORES.FAIL,
     };
 
     const wrapper = mount(
       Provider(
         { store },
         AuditFilter(
           {
             checks: { CONTRAST },
--- a/devtools/client/accessibility/test/jest/components/badges.test.js
+++ b/devtools/client/accessibility/test/jest/components/badges.test.js
@@ -15,16 +15,20 @@ const {
 } = require("devtools/client/accessibility/test/jest/helpers");
 
 const Badge = require("devtools/client/accessibility/components/Badge");
 const Badges = createFactory(
   require("devtools/client/accessibility/components/Badges")
 );
 const ContrastBadge = require("devtools/client/accessibility/components/ContrastBadge");
 
+const {
+  accessibility: { SCORES },
+} = require("devtools/shared/constants");
+
 describe("Badges component:", () => {
   const store = setupStore();
 
   it("no props render", () => {
     const wrapper = mount(Provider({ store }, Badges()));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
@@ -53,33 +57,33 @@ describe("Badges component:", () => {
         { store },
         Badges({
           checks: {
             CONTRAST: {
               value: 5.11,
               color: [255, 0, 0, 1],
               backgroundColor: [255, 255, 255, 1],
               isLargeText: false,
-              score: "AA",
+              score: SCORES.AA,
             },
           },
         })
       )
     );
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("contrast ratio fail render", () => {
     const CONTRAST = {
       value: 3.1,
       color: [255, 0, 0, 1],
       backgroundColor: [255, 255, 255, 1],
       isLargeText: false,
-      score: "fail",
+      score: SCORES.FAIL,
     };
     const wrapper = mount(
       Provider({ store }, Badges({ checks: { CONTRAST } }))
     );
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.find(Badge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).length).toBe(1);
@@ -94,17 +98,17 @@ describe("Badges component:", () => {
   it("contrast ratio fail range render", () => {
     const CONTRAST = {
       min: 1.19,
       max: 1.39,
       color: [128, 128, 128, 1],
       backgroundColorMin: [219, 106, 116, 1],
       backgroundColorMax: [156, 145, 211, 1],
       isLargeText: false,
-      score: "fail",
+      score: SCORES.FAIL,
     };
     const wrapper = mount(
       Provider({ store }, Badges({ checks: { CONTRAST } }))
     );
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.find(Badge).length).toBe(1);
     expect(wrapper.find(ContrastBadge).length).toBe(1);
--- a/devtools/client/accessibility/test/jest/components/contrast-badge.test.js
+++ b/devtools/client/accessibility/test/jest/components/contrast-badge.test.js
@@ -14,70 +14,74 @@ const Provider = createFactory(
 const {
   setupStore,
 } = require("devtools/client/accessibility/test/jest/helpers");
 
 const Badge = require("devtools/client/accessibility/components/Badge");
 const ContrastBadgeClass = require("devtools/client/accessibility/components/ContrastBadge");
 const ContrastBadge = createFactory(ContrastBadgeClass);
 
+const {
+  accessibility: { SCORES },
+} = require("devtools/shared/constants");
+
 describe("ContrastBadge component:", () => {
   const store = setupStore();
 
   it("error render", () => {
     const wrapper = shallow(ContrastBadge({ error: true }));
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success render", () => {
     const wrapper = shallow(
       ContrastBadge({
         value: 5.11,
         isLargeText: false,
-        score: "AA",
+        score: SCORES.AA,
       })
     );
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success range render", () => {
     const wrapper = shallow(
       ContrastBadge({
         min: 5.11,
         max: 6.25,
         isLargeText: false,
-        score: "AA",
+        score: SCORES.AA,
       })
     );
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("success large text render", () => {
     const wrapper = shallow(
       ContrastBadge({
         value: 3.77,
         isLargeText: true,
-        score: "AA",
+        score: SCORES.AA,
       })
     );
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.isEmptyRender()).toBe(true);
   });
 
   it("fail render", () => {
     const wrapper = mount(
       Provider(
         { store },
         ContrastBadge({
           value: 3.77,
           isLargeText: false,
-          score: "fail",
+          score: SCORES.FAIL,
         })
       )
     );
 
     expect(wrapper.html()).toMatchSnapshot();
     expect(wrapper.children().length).toBe(1);
     const contrastBadge = wrapper.find(ContrastBadgeClass);
     const badge = contrastBadge.childAt(0);
--- a/devtools/client/accessibility/test/jest/components/text-label-check.test.js
+++ b/devtools/client/accessibility/test/jest/components/text-label-check.test.js
@@ -38,17 +38,17 @@ function testTextLabelCheck(wrapper, pro
   expect(localized.length).toBe(3);
 
   const heading = localized.at(0).childAt(0);
   expect(heading.type()).toBe("h3");
   expect(heading.hasClass("accessibility-check-header")).toBe(true);
 
   const icon = localized.at(1).childAt(0);
   expect(icon.type()).toBe("img");
-  expect(icon.hasClass(props.score === FAIL ? "fail" : props.score)).toBe(true);
+  expect(icon.hasClass(props.score)).toBe(true);
 
   const annotation = localized.at(2).childAt(0);
   expect(annotation.type()).toBe("p");
   expect(annotation.hasClass("accessibility-check-annotation")).toBe(true);
 }
 
 describe("TextLabelCheck component:", () => {
   const testProps = [
--- a/devtools/client/accessibility/test/mochitest/contrast.snapshots.js
+++ b/devtools/client/accessibility/test/mochitest/contrast.snapshots.js
@@ -56,17 +56,17 @@ window._snapshots = {
         props: {
           role: "presentation",
           className: "accessibility-color-contrast",
         },
         children: [
           {
             type: "span",
             props: {
-              className: "accessibility-contrast-value fail",
+              className: "accessibility-contrast-value FAIL",
               role: "presentation",
               style: {
                 "--accessibility-contrast-color": "rgba(255,0,0,1)",
                 "--accessibility-contrast-bg": "rgba(255,255,255,1)",
               },
             },
             children: ["4.00"],
           },
@@ -117,17 +117,17 @@ window._snapshots = {
         props: {
           role: "presentation",
           className: "accessibility-color-contrast",
         },
         children: [
           {
             type: "span",
             props: {
-              className: "accessibility-contrast-value fail",
+              className: "accessibility-contrast-value FAIL",
               role: "presentation",
               style: {
                 "--accessibility-contrast-color": "rgba(128,128,128,1)",
                 "--accessibility-contrast-bg": "rgba(219,106,116,1)",
               },
             },
             children: ["1.19"],
           },
@@ -137,17 +137,17 @@ window._snapshots = {
               role: "presentation",
               className: "accessibility-color-contrast-separator",
             },
             children: null,
           },
           {
             type: "span",
             props: {
-              className: "accessibility-contrast-value fail",
+              className: "accessibility-contrast-value FAIL",
               role: "presentation",
               style: {
                 "--accessibility-contrast-color": "rgba(128,128,128,1)",
                 "--accessibility-contrast-bg": "rgba(156,145,211,1)",
               },
             },
             children: ["1.39"],
           },
--- a/devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
+++ b/devtools/client/accessibility/test/mochitest/test_accessible_contrast.html
@@ -26,51 +26,55 @@ Test that Color Contrast component rende
 /* global matchSnapshot */
 
 window.onload = async function() {
   try {
     const React = browserRequire("devtools/client/shared/vendor/react");
     const { ColorContrastCheck } = browserRequire(
       "devtools/client/accessibility/components/ColorContrastAccessibility");
 
+    const {
+      accessibility: { SCORES },
+    } = browserRequire("devtools/shared/constants");
+
     matchSnapshot("ColorContrastAccessibility error render.",
       React.createElement(ColorContrastCheck, { error: true })
     );
 
     matchSnapshot("ColorContrastAccessibility basic render.",
       React.createElement(ColorContrastCheck, {
         "value": 4.00,
         "color": [255, 0, 0, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": false,
-        "score": "fail",
+        "score": SCORES.FAIL,
       })
     );
 
     matchSnapshot("ColorContrastAccessibility range render.",
       React.createElement(ColorContrastCheck, {
         "min": 1.19,
         "max": 1.39,
         "color": [128, 128, 128, 1],
         "backgroundColorMin": [219, 106, 116, 1],
         "backgroundColorMax": [156, 145, 211, 1],
         "isLargeText": false,
-        "score": "fail",
-        "scoreMin": "fail",
-        "scoreMax": "fail",
+        "score": SCORES.FAIL,
+        "scoreMin": SCORES.FAIL,
+        "scoreMax": SCORES.FAIL,
       })
     );
 
     matchSnapshot("ColorContrastAccessibility large text render.",
       React.createElement(ColorContrastCheck, {
         "value": 4.00,
         "color": [255, 0, 0, 1],
         "backgroundColor": [255, 255, 255, 1],
         "isLargeText": true,
-        "score": "AA",
+        "score": SCORES.AA,
       })
     );
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 };
--- a/devtools/client/locales/en-US/accessibility.properties
+++ b/devtools/client/locales/en-US/accessibility.properties
@@ -141,21 +141,21 @@ accessibility.contrast.large.title=Text 
 accessibility.contrast.annotation.AA=Meets WCAG AA standards for accessible text. %S
 
 # LOCALIZATION NOTE (accessibility.contrast.annotation.AAA): A title text for the
 # paragraph describing that the given colour contrast satisfies AAA standard from Web
 # Content Accessibility Guidelines. %S in the content will be replaced by a link at run
 # time with the accessibility.learnMore string.
 accessibility.contrast.annotation.AAA=Meets WCAG AAA standards for accessible text. %S
 
-# LOCALIZATION NOTE (accessibility.contrast.annotation.fail): A title text for the
+# LOCALIZATION NOTE (accessibility.contrast.annotation.FAIL): A title text for the
 # paragraph describing that the given colour contrast fails to meet the minimum level from
 # Web Content Accessibility Guidelines. %S in the content will be replaced by a link at
 # run time with the accessibility.learnMore string.
-accessibility.contrast.annotation.fail=Does not meet WCAG standards for accessible text. %S
+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.
--- a/devtools/client/themes/accessibility-color-contrast.css
+++ b/devtools/client/themes/accessibility-color-contrast.css
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 /* Classes used to style the color contrast section in the Accessibility
  * Checks panel and color picker tooltip across the Inspector panel.
  *
  * The section consists of:
- *   - contrast ratio value (numeric + score badge (AA/AAA/fail)):
+ *   - contrast ratio value (numeric + score badge (AA/AAA/FAIL)):
  *       Only shows up if contrast ratio can be calculated.
  *   - large text indicator badge:
  *       Only shows up if the selected text node contains large text.
  */
 .accessibility-color-contrast {
   position: relative;
   display: flex;
   cursor: default;
@@ -26,17 +26,17 @@
 .accessibility-color-contrast
   .accessibility-contrast-value:not(:empty).AA:after,
 .accessibility-color-contrast
   .accessibility-contrast-value:not(:empty).AAA:after {
   color: var(--theme-highlight-green);
 }
 
 .accessibility-color-contrast
-  .accessibility-contrast-value:not(:empty).fail:after {
+  .accessibility-contrast-value:not(:empty).FAIL:after {
   color: #e57180;
   content: "⚠️";
 }
 
 .accessibility-color-contrast
   .accessibility-contrast-value:not(:empty).AA:after {
   content: "AA\2713";
   unicode-bidi: isolate;
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -738,84 +738,93 @@
   fill: #6a5acd;
 }
 
 :-moz-native-anonymous .accessible-infobar-name,
 :-moz-native-anonymous .accessible-infobar-audit {
   color: var(--highlighter-infobar-color);
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty):before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty)::before {
   content: "";
   height: 8px;
   width: 8px;
   display: inline-flex;
   background-color: var(--accessibility-highlighter-contrast-ratio-color);
   box-shadow: 0 0 0 1px var(--grey-40), 4px 3px var(--accessibility-highlighter-contrast-ratio-bg), 4px 3px 0 1px var(--grey-40);
   margin-inline-start: 3px;
   margin-inline-end: 9px;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty):after {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty)::after {
   margin-inline-start: 2px;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA:after,
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA:after {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA::after,
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA::after {
   color: #90E274;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).fail:after {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).FAIL::after {
   color: #E57180;
   content: "⚠️";
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA:after {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA::after {
   content: "AA\2713";
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA:after {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA::after {
   content: "AAA\2713";
 }
 
 :-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-label,
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-separator:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-separator::before {
   margin-inline-end: 3px;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-separator:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-contrast-ratio-separator::before {
   content: "-";
   margin-inline-start: 3px;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit {
+  display: block;
+  padding-block-end: 5px;
+}
+
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit:last-child {
+  padding-block-end: 0;
+}
+
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit::before {
   display: inline-block;
   width: 12px;
   height: 12px;
   content: "";
   margin-inline-end: 4px;
   vertical-align: -2px;
   background-image: none;
   background-position: center;
   background-repeat: no-repeat;
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.fail:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit.FAIL::before {
   background-image: url(chrome://devtools/skin/images/error-small.svg);
   fill: var(--red-40);
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.WARNING:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit.WARNING::before {
   background-image: url(chrome://devtools/skin/images/alert-small.svg);
   fill: var(--yellow-60);
 }
 
-:-moz-native-anonymous .accessible-infobar-audit .accessible-text-label.BEST_PRACTICES:before {
+:-moz-native-anonymous .accessible-infobar-audit .accessible-audit.BEST_PRACTICES::before {
   background-image: url(chrome://devtools/skin/images/info-small.svg);
 }
 
 :-moz-native-anonymous .accessible-infobar-name:not(:empty) {
   border-inline-start: 1px solid #5a6169;
   margin-inline-start: 6px;
   padding-inline-start: 6px;
 }
--- a/devtools/server/actors/highlighters/utils/accessibility.js
+++ b/devtools/server/actors/highlighters/utils/accessibility.js
@@ -24,16 +24,23 @@ DevToolsUtils.defineLazyGetter(
   "L10N",
   () => new LocalizationHelper(STRINGS_URI)
 );
 
 const {
   accessibility: {
     AUDIT_TYPE,
     ISSUE_TYPE: {
+      [AUDIT_TYPE.KEYBOARD]: {
+        FOCUSABLE_NO_SEMANTICS,
+        FOCUSABLE_POSITIVE_TABINDEX,
+        INTERACTIVE_NO_ACTION,
+        INTERACTIVE_NOT_FOCUSABLE,
+        NO_FOCUS_VISIBLE,
+      },
       [AUDIT_TYPE.TEXT_LABEL]: {
         AREA_NO_NAME_FROM_ALT,
         DIALOG_NO_NAME,
         DOCUMENT_NO_TITLE,
         EMBED_NO_NAME,
         FIGURE_NO_NAME,
         FORM_FIELDSET_NO_NAME,
         FORM_FIELDSET_NO_NAME_FROM_LEGEND,
@@ -415,17 +422,21 @@ class XULWindowInfobar extends Infobar {
  * display various audit information such as contrast ratio score.
  */
 class Audit {
   constructor(infobar) {
     this.infobar = infobar;
 
     // A list of audit reports to be shown on the fly when highlighting an accessible
     // object.
-    this.reports = [new ContrastRatio(this), new TextLabel(this)];
+    this.reports = {
+      [AUDIT_TYPE.CONTRAST]: new ContrastRatio(this),
+      [AUDIT_TYPE.KEYBOARD]: new Keyboard(this),
+      [AUDIT_TYPE.TEXT_LABEL]: new TextLabel(this),
+    };
   }
 
   get prefix() {
     return this.infobar.prefix;
   }
 
   get win() {
     return this.infobar.win;
@@ -437,25 +448,25 @@ class Audit {
       parent: root,
       attributes: {
         class: "infobar-audit",
         id: "infobar-audit",
       },
       prefix: this.prefix,
     });
 
-    this.reports.forEach(report => report.buildMarkup(audit));
+    Object.values(this.reports).forEach(report => report.buildMarkup(audit));
   }
 
   update(audit = {}) {
     const el = this.getElement("infobar-audit");
     el.setAttribute("hidden", true);
 
     let updated = false;
-    this.reports.forEach(report => {
+    Object.values(this.reports).forEach(report => {
       if (report.update(audit)) {
         updated = true;
       }
     });
 
     if (updated) {
       el.removeAttribute("hidden");
     }
@@ -466,17 +477,17 @@ class Audit {
   }
 
   setTextContent(el, text) {
     return this.infobar.setTextContent(el, text);
   }
 
   destroy() {
     this.infobar = null;
-    this.reports.forEach(report => report.destroy());
+    Object.values(this.reports).forEach(report => report.destroy());
     this.reports = null;
   }
 }
 
 /**
  * A common interface between audit report components used to render accessibility audit
  * information for the currently highlighted accessible object.
  */
@@ -655,16 +666,80 @@ class ContrastRatio extends AuditReport 
       backgroundColor: backgroundColorMax,
     });
 
     return true;
   }
 }
 
 /**
+ * Keyboard audit report that is used to display a problem with keyboard
+ * accessibility as part of the inforbar.
+ */
+class Keyboard extends AuditReport {
+  /**
+   * A map from keyboard issues to annotation component properties.
+   */
+  static get ISSUE_TO_INFOBAR_LABEL_MAP() {
+    return {
+      [FOCUSABLE_NO_SEMANTICS]: "accessibility.keyboard.issue.semantics",
+      [FOCUSABLE_POSITIVE_TABINDEX]: "accessibility.keyboard.issue.tabindex",
+      [INTERACTIVE_NO_ACTION]: "accessibility.keyboard.issue.action",
+      [INTERACTIVE_NOT_FOCUSABLE]: "accessibility.keyboard.issue.focusable",
+      [NO_FOCUS_VISIBLE]: "accessibility.keyboard.issue.focus.visible",
+    };
+  }
+
+  buildMarkup(root) {
+    createNode(this.win, {
+      nodeType: "span",
+      parent: root,
+      attributes: {
+        class: "audit",
+        id: "keyboard",
+      },
+      prefix: this.prefix,
+    });
+  }
+
+  /**
+   * Update keyboard audit infobar markup.
+   * @param  {Object}
+   *         Audit report for a given highlighted accessible.
+   * @return {Boolean}
+   *         True if the keyboard markup was updated correctly and infobar audit
+   *         block should be visible.
+   */
+  update(audit) {
+    const el = this.getElement("keyboard");
+    el.setAttribute("hidden", true);
+    Object.values(SCORES).forEach(className => el.classList.remove(className));
+
+    if (!audit) {
+      return false;
+    }
+
+    const keyboardAudit = audit[AUDIT_TYPE.KEYBOARD];
+    if (!keyboardAudit) {
+      return false;
+    }
+
+    const { issue, score } = keyboardAudit;
+    this.setTextContent(
+      el,
+      L10N.getStr(Keyboard.ISSUE_TO_INFOBAR_LABEL_MAP[issue])
+    );
+    el.classList.add(score);
+    el.removeAttribute("hidden");
+
+    return true;
+  }
+}
+
+/**
  * Text label audit report that is used to display a problem with text alternatives
  * as part of the inforbar.
  */
 class TextLabel extends AuditReport {
   /**
    * A map from text label issues to annotation component properties.
    */
   static get ISSUE_TO_INFOBAR_LABEL_MAP() {
@@ -692,17 +767,17 @@ class TextLabel extends AuditReport {
     };
   }
 
   buildMarkup(root) {
     createNode(this.win, {
       nodeType: "span",
       parent: root,
       attributes: {
-        class: "text-label",
+        class: "audit",
         id: "text-label",
       },
       prefix: this.prefix,
     });
   }
 
   /**
    * Update text label audit infobar markup.
--- a/devtools/server/actors/highlighters/xul-accessible.js
+++ b/devtools/server/actors/highlighters/xul-accessible.js
@@ -97,92 +97,101 @@ const ACCESSIBLE_BOUNDS_SHEET =
     padding-bottom: 2px;
   }
 
   .accessible-infobar-name,
   .accessible-infobar-audit {
     color: hsl(210, 30%, 85%);
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty):before {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty)::before {
     content: "";
     height: 8px;
     width: 8px;
     display: inline-flex;
     background-color: var(--accessibility-highlighter-contrast-ratio-color);
     box-shadow: 0 0 0 1px var(--grey-40),
                 4px 3px var(--accessibility-highlighter-contrast-ratio-bg),
                 4px 3px 0 1px var(--grey-40);
     margin-inline-start: 3px;
     margin-inline-end: 9px;
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty):after {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty)::after {
     margin-inline-start: 2px;
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA:after,
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA:after {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA::after,
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA::after {
     color: #90E274;
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).fail:after {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).FAIL::after {
     color: #E57180;
     content: "⚠️";
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA:after {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AA::after {
     content: "AA\u2713";
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA:after {
+  .accessible-infobar-audit .accessible-contrast-ratio:not(:empty).AAA::after {
     content: "AAA\u2713";
   }
 
   .accessible-infobar-audit .accessible-contrast-ratio-label,
-  .accessible-infobar-audit .accessible-contrast-ratio-separator:before {
+  .accessible-infobar-audit .accessible-contrast-ratio-separator::before {
     margin-inline-end: 3px;
   }
 
-  .accessible-infobar-audit .accessible-contrast-ratio-separator:before {
+  .accessible-infobar-audit .accessible-contrast-ratio-separator::before {
     content: "-";
     margin-inline-start: 3px;
   }
 
   .accessible-infobar-name:not(:empty) {
     border-inline-start: 1px solid #5a6169;
     margin-inline-start: 6px;
     padding-inline-start: 6px;
   }
 
-  .accessible-infobar-audit .accessible-text-label:before {
+  .accessible-infobar-audit .accessible-audit {
+    display: block;
+    padding-block-end: 5px;
+  }
+
+  .accessible-infobar-audit .accessible-audit:last-child {
+    padding-block-end: 0;
+  }
+
+  .accessible-infobar-audit .accessible-audit::before {
     display: inline-block;
     width: 12px;
     height: 12px;
     content: "";
     margin-inline-end: 4px;
     vertical-align: -2px;
     background-image: none;
     background-position: center;
     background-repeat: no-repeat;
     -moz-context-properties: fill;
     fill: currentColor;
   }
 
-  .accessible-infobar-audit .accessible-text-label.fail:before {
+  .accessible-infobar-audit .accessible-audit.FAIL::before {
     background-image: url(chrome://devtools/skin/images/error-small.svg);
     fill: var(--red-40);
   }
 
-  .accessible-infobar-audit .accessible-text-label.WARNING:before {
+  .accessible-infobar-audit .accessible-audit.WARNING::before {
     background-image: url(chrome://devtools/skin/images/alert-small.svg);
     fill: var(--yellow-60);
   }
 
-  .accessible-infobar-audit .accessible-text-label.BEST_PRACTICES:before {
+  .accessible-infobar-audit .accessible-audit.BEST_PRACTICES::before {
     background-image: url(chrome://devtools/skin/images/info-small.svg);
   }`);
 
 /**
  * The XULWindowAccessibleHighlighter is a class that has the same API as the
  * AccessibleHighlighter, and by extension other highlighters that implement
  * auto-refresh highlighter, but instead of drawing in canvas frame anonymous
  * content (that is not available for chrome accessible highlighting) it adds a
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -49,16 +49,17 @@ support-files =
 
 [browser_accessibility_highlighter_infobar.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_infobar_show.js]
 [browser_accessibility_keyboard_audit.js]
 skip-if =
   (os == 'win' && processor == 'aarch64') || # bug 1533184
   fission # Fails intermittently under Fission.
+[browser_accessibility_infobar_audit_keyboard.js]
 [browser_accessibility_infobar_audit_text_label.js]
 [browser_accessibility_node.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_node_audit.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_node_events.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_simple.js]
copy from devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
copy to devtools/server/tests/browser/browser_accessibility_infobar_audit_keyboard.js
--- a/devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
+++ b/devtools/server/tests/browser/browser_accessibility_infobar_audit_keyboard.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 "use strict";
 
-// Checks for the AccessibleHighlighter's infobar component and its text label
+// Checks for the AccessibleHighlighter's infobar component and its keyboard
 // audit.
 
 add_task(async function() {
   await BrowserTestUtils.withNewTab(
     {
       gBrowser,
       url: MAIN_DOMAIN + "doc_accessibility_infobar.html",
     },
@@ -28,55 +28,50 @@ add_task(async function() {
         const L10N = new LocalizationHelper(
           "devtools/shared/locales/accessibility.properties"
         );
 
         const {
           accessibility: {
             AUDIT_TYPE,
             ISSUE_TYPE: {
-              [AUDIT_TYPE.TEXT_LABEL]: {
-                DIALOG_NO_NAME,
-                FORM_NO_VISIBLE_NAME,
-                TOOLBAR_NO_NAME,
+              [AUDIT_TYPE.KEYBOARD]: {
+                INTERACTIVE_NO_ACTION,
+                FOCUSABLE_NO_SEMANTICS,
               },
             },
-            SCORES: { BEST_PRACTICES, FAIL, WARNING },
+            SCORES: { FAIL, WARNING },
           },
         } = require("devtools/shared/constants");
 
         /**
          * Checks for updated content for an infobar.
          *
          * @param  {Object} infobar
          *         Accessible highlighter's infobar component.
          * @param  {Object} audit
          *         Audit information that is passed on highlighter show.
          */
-        function checkTextLabel(infobar, audit) {
+        function checkKeyboard(infobar, audit) {
           const { issue, score } = audit || {};
           let expected = "";
           if (issue) {
-            const {
-              ISSUE_TO_INFOBAR_LABEL_MAP,
-            } = infobar.audit.reports[1].constructor;
+            const { ISSUE_TO_INFOBAR_LABEL_MAP } = infobar.audit.reports[
+              AUDIT_TYPE.KEYBOARD
+            ].constructor;
             expected = L10N.getStr(ISSUE_TO_INFOBAR_LABEL_MAP[issue]);
           }
 
           is(
-            infobar.getTextContent("text-label"),
+            infobar.getTextContent("keyboard"),
             expected,
-            "infobar text label audit text content is correct"
+            "infobar keyboard audit text content is correct"
           );
           if (score) {
-            ok(
-              infobar
-                .getElement("text-label")
-                .classList.contains(score === FAIL ? "fail" : score)
-            );
+            ok(infobar.getElement("keyboard").classList.contains(score));
           }
         }
 
         // Start testing. First, create highlighter environment and initialize.
         const env = new HighlighterEnvironment();
         env.initFromWindow(content.window);
 
         // Wait for loading highlighter environment content to complete before creating the
@@ -110,62 +105,53 @@ add_task(async function() {
           y: 0,
           w: 250,
           h: 100,
         };
 
         const tests = [
           {
             desc:
-              "Infobar is shown with no text label audit content when no audit.",
+              "Infobar is shown with no keyboard audit content when no audit.",
           },
           {
             desc:
-              "Infobar is shown with no text label audit content when audit is null.",
+              "Infobar is shown with no keyboard audit content when audit is null.",
             audit: null,
           },
           {
             desc:
-              "Infobar is shown with no text label audit content when empty " +
-              "text label audit.",
-            audit: { [AUDIT_TYPE.TEXT_LABEL]: null },
+              "Infobar is shown with no keyboard audit content when empty " +
+              "keyboard audit.",
+            audit: { [AUDIT_TYPE.KEYBOARD]: null },
           },
           {
-            desc:
-              "Infobar is shown with text label audit content for an error.",
+            desc: "Infobar is shown with keyboard audit content for an error.",
             audit: {
-              [AUDIT_TYPE.TEXT_LABEL]: { score: FAIL, issue: TOOLBAR_NO_NAME },
-            },
-          },
-          {
-            desc:
-              "Infobar is shown with text label audit content for a warning.",
-            audit: {
-              [AUDIT_TYPE.TEXT_LABEL]: {
-                score: WARNING,
-                issue: FORM_NO_VISIBLE_NAME,
+              [AUDIT_TYPE.KEYBOARD]: {
+                score: FAIL,
+                issue: INTERACTIVE_NO_ACTION,
               },
             },
           },
           {
-            desc:
-              "Infobar is shown with text label audit content for best practices.",
+            desc: "Infobar is shown with keyboard audit content for a warning.",
             audit: {
-              [AUDIT_TYPE.TEXT_LABEL]: {
-                score: BEST_PRACTICES,
-                issue: DIALOG_NO_NAME,
+              [AUDIT_TYPE.KEYBOARD]: {
+                score: WARNING,
+                issue: FOCUSABLE_NO_SEMANTICS,
               },
             },
           },
         ];
 
         for (const test of tests) {
           const { desc, audit } = test;
 
           info(desc);
           highlighter.show(node, { ...bounds, audit });
-          checkTextLabel(infobar, audit && audit[AUDIT_TYPE.TEXT_LABEL]);
+          checkKeyboard(infobar, audit && audit[AUDIT_TYPE.KEYBOARD]);
           highlighter.hide();
         }
       });
     }
   );
 });
--- a/devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
+++ b/devtools/server/tests/browser/browser_accessibility_infobar_audit_text_label.js
@@ -50,33 +50,29 @@ add_task(async function() {
          *         Accessible highlighter's infobar component.
          * @param  {Object} audit
          *         Audit information that is passed on highlighter show.
          */
         function checkTextLabel(infobar, audit) {
           const { issue, score } = audit || {};
           let expected = "";
           if (issue) {
-            const {
-              ISSUE_TO_INFOBAR_LABEL_MAP,
-            } = infobar.audit.reports[1].constructor;
+            const { ISSUE_TO_INFOBAR_LABEL_MAP } = infobar.audit.reports[
+              AUDIT_TYPE.TEXT_LABEL
+            ].constructor;
             expected = L10N.getStr(ISSUE_TO_INFOBAR_LABEL_MAP[issue]);
           }
 
           is(
             infobar.getTextContent("text-label"),
             expected,
             "infobar text label audit text content is correct"
           );
           if (score) {
-            ok(
-              infobar
-                .getElement("text-label")
-                .classList.contains(score === FAIL ? "fail" : score)
-            );
+            ok(infobar.getElement("text-label").classList.contains(score));
           }
         }
 
         // Start testing. First, create highlighter environment and initialize.
         const env = new HighlighterEnvironment();
         env.initFromWindow(content.window);
 
         // Wait for loading highlighter environment content to complete before creating the
--- a/devtools/server/tests/browser/browser_accessibility_node_audit.js
+++ b/devtools/server/tests/browser/browser_accessibility_node_audit.js
@@ -6,29 +6,29 @@
 
 /**
  * Checks functionality around audit for the AccessibleActor. This includes
  * tests for the return value when calling the audit method, payload of the
  * corresponding event as well as the AccesibleFront state being up to date.
  */
 
 const {
-  accessibility: { AUDIT_TYPE },
+  accessibility: { AUDIT_TYPE, SCORES },
 } = require("devtools/shared/constants");
 const EMPTY_AUDIT = Object.keys(AUDIT_TYPE).reduce((audit, key) => {
   audit[key] = null;
   return audit;
 }, {});
 
 const EXPECTED_CONTRAST_DATA = {
   value: 21,
   color: [0, 0, 0, 1],
   backgroundColor: [255, 255, 255, 1],
   isLargeText: true,
-  score: "AAA",
+  score: SCORES.AAA,
 };
 
 const EMPTY_CONTRAST_AUDIT = {
   [AUDIT_TYPE.CONTRAST]: null,
 };
 
 const CONTRAST_AUDIT = {
   [AUDIT_TYPE.CONTRAST]: EXPECTED_CONTRAST_DATA,
--- a/devtools/server/tests/browser/browser_accessibility_walker_audit.js
+++ b/devtools/server/tests/browser/browser_accessibility_walker_audit.js
@@ -47,17 +47,17 @@ add_task(async function() {
       role: "text leaf",
       childCount: 0,
       checks: {
         [AUDIT_TYPE.CONTRAST]: {
           value: 4.0,
           color: [255, 0, 0, 1],
           backgroundColor: [255, 255, 255, 1],
           isLargeText: false,
-          score: "fail",
+          score: SCORES.FAIL,
         },
         [AUDIT_TYPE.KEYBOARD]: null,
         [AUDIT_TYPE.TEXT_LABEL]: null,
       },
     },
     {
       name: "",
       role: "paragraph",
@@ -73,17 +73,17 @@ add_task(async function() {
       role: "text leaf",
       childCount: 0,
       checks: {
         [AUDIT_TYPE.CONTRAST]: {
           value: 4.0,
           color: [255, 0, 0, 1],
           backgroundColor: [255, 255, 255, 1],
           isLargeText: false,
-          score: "fail",
+          score: SCORES.FAIL,
         },
         [AUDIT_TYPE.KEYBOARD]: null,
         [AUDIT_TYPE.TEXT_LABEL]: null,
       },
     },
   ];
   const total = accessibles.length;
   const auditProgress = [
--- a/devtools/shared/constants.js
+++ b/devtools/shared/constants.js
@@ -75,17 +75,17 @@ const ISSUE_TYPE = {
 const SCORES = {
   // Satisfies WCAG AA guidelines.
   AA: "AA",
   // Satisfies WCAG AAA guidelines.
   AAA: "AAA",
   // Elevates accessibility experience.
   BEST_PRACTICES: "BEST_PRACTICES",
   // Does not satisfy the baseline WCAG guidelines.
-  FAIL: "fail",
+  FAIL: "FAIL",
   // Partially satisfies the WCAG AA guidelines.
   WARNING: "WARNING",
 };
 
 exports.accessibility = {
   AUDIT_TYPE,
   ISSUE_TYPE,
   SCORES,
--- a/devtools/shared/locales/en-US/accessibility.properties
+++ b/devtools/shared/locales/en-US/accessibility.properties
@@ -104,8 +104,34 @@ accessibility.text.label.issue.interacti
 # describes that currently selected accessible object for an <optgroup> must have a
 # name provided via label attribute.
 accessibility.text.label.issue.optgroup.label2 = Use a “label” attribute to label an “optgroup”.
 
 # LOCALIZATION NOTE (accessibility.text.label.issue.toolbar): A title text that
 # describes that currently selected accessible object for a toolbar must have a
 # name provided when there is more than one toolbar in the document.
 accessibility.text.label.issue.toolbar = Toolbars must be labeled when there is more than one toolbar.
+
+# LOCALIZATION NOTE (accessibility.keyboard.issue.semantics): A title text that
+# describes that currently selected accessible object is focusable and should
+# indicate that it could be interacted with.
+accessibility.keyboard.issue.semantics=Focusable elements should have interactive semantics.
+
+# LOCALIZATION NOTE (accessibility.keyboard.issue.tabindex): A title text that
+# describes that currently selected accessible object has a corresponding
+# DOMNode that defines a tabindex attribute greater that 0 which can result in
+# unexpected behaviour when navigating with keyboard.
+accessibility.keyboard.issue.tabindex=Avoid using “tabindex” attribute greater than zero.
+
+# LOCALIZATION NOTE (accessibility.keyboard.issue.action): A title text that
+# describes that currently selected accessible object is interactive but can not
+# be activated using keyboard or accessibility API.
+accessibility.keyboard.issue.action=Interactive elements must be able to be activated using a keyboard.
+
+# LOCALIZATION NOTE (accessibility.keyboard.issue.focusable): A title text that
+# describes that currently selected accessible object is interactive but is not
+# focusable with a keyboard.
+accessibility.keyboard.issue.focusable=Interactive elements must be focusable.
+
+# LOCALIZATION NOTE (accessibility.keyboard.issue.focus.visible): A title text
+# that describes that currently selected accessible object is focusable but
+# might not have appropriate focus styling.
+accessibility.keyboard.issue.focus.visible=Focusable element may be missing focus styling.