diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json index b4d573c3d4c0..64ca6164e84b 100644 --- a/lighthouse-core/lib/i18n/en-US.json +++ b/lighthouse-core/lib/i18n/en-US.json @@ -1435,6 +1435,22 @@ "message": "Passed audits", "description": "Section heading shown above a list of audits that are passing. 'Passed' here refers to a passing grade. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list." }, + "lighthouse-core/report/html/renderer/util.js | quickTabFirstAudit": { + "message": "Skip to first audit", + "description": "This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element." + }, + "lighthouse-core/report/html/renderer/util.js | quickTabFirstAverageAudit": { + "message": "Skip to first average audit", + "description": "This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element." + }, + "lighthouse-core/report/html/renderer/util.js | quickTabFirstCategory": { + "message": "Skip to first category", + "description": "This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element." + }, + "lighthouse-core/report/html/renderer/util.js | quickTabFirstFailingAudit": { + "message": "Skip to first failing audit", + "description": "This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element." + }, "lighthouse-core/report/html/renderer/util.js | snippetCollapseButtonLabel": { "message": "Collapse snippet", "description": "Label for button that only shows a few lines of the snippet when clicked" diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index df9583dacf84..8de07c6aee73 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -84,6 +84,7 @@ class ReportUIFeatures { this._setupToolsButton(); this._setupThirdPartyFilter(); this._setUpCollapseDetailsAfterPrinting(); + this._setUpQuickTabFocus(); this._resetUIState(); this._document.addEventListener('keyup', this.onKeyUp); this._document.addEventListener('copy', this.onCopy); @@ -558,6 +559,58 @@ class ReportUIFeatures { } } + _getFirstInterestingElement() { + const rules = [ + { + selector: '.lh-audit--fail summary', + label: Util.UIStrings.quickTabFirstFailingAudit, + }, + { + selector: '.lh-audit--average summary', + label: Util.UIStrings.quickTabFirstAverageAudit, + }, + { + selector: '.lh-audit--audit summary', + label: Util.UIStrings.quickTabFirstAudit, + }, + { + selector: '.lh-category-header .lh-gauge__wrapper', + label: Util.UIStrings.quickTabFirstCategory, + }, + ]; + + const firstValidRule = rules.find(({selector}) => { + const el = this._document.querySelector(selector); + return Boolean(el); + }); + + if (!firstValidRule) { + return null; + } + + return { + element: this._dom.find(firstValidRule.selector, this._document), + label: firstValidRule.label, + }; + } + + /** + * Tab + Enter for quick jump document.activeElement to the first interesting element. + */ + _setUpQuickTabFocus() { + const firstInterestingElement = this._getFirstInterestingElement(); + if (!firstInterestingElement) return; + const {element, label} = firstInterestingElement; + + const quickFocusEl = this._dom.find('.lh-a11y-quick-focus', this._document); + quickFocusEl.textContent = label; + quickFocusEl.classList.remove('disabled'); + quickFocusEl.addEventListener('click', (e) => { + element.focus(); + e.preventDefault(); + }); + } + /** * Returns the html that recreates this report. * @return {string} diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index f29b6293374a..39a0a50396d9 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -600,6 +600,18 @@ Util.UIStrings = { /** This label is for a checkbox above a table of items loaded by a web page. The checkbox is used to show or hide third-party (or "3rd-party") resources in the table, where "third-party resources" refers to items loaded by a web page from URLs that aren't controlled by the owner of the web page. */ thirdPartyResourcesLabel: 'Show 3rd-party resources', + + /** This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element. */ + quickTabFirstFailingAudit: 'Skip to first failing audit', + + /** This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element. */ + quickTabFirstAverageAudit: 'Skip to first average audit', + + /** This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element. */ + quickTabFirstAudit: 'Skip to first audit', + + /** This label is shown to users who tab-focus to navigate the Lighthouse report. The first focus element on the page will show this text, and hitting enter will move the document's focus to the specified element. */ + quickTabFirstCategory: 'Skip to first category', }; if (typeof module !== 'undefined' && module.exports) { diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html index 0323cf54db7d..cd95dac9b33d 100644 --- a/lighthouse-core/report/html/templates.html +++ b/lighthouse-core/report/html/templates.html @@ -361,8 +361,32 @@ margin-left: 0; } } + + .lh-a11y-quick-focus { + background-color: var(--color-blue); + color: var(--color-white); + padding: 20px; + clip: rect(1px, 1px, 1px, 1px); + margin: 0; + height: 1px; + width: 1px; + overflow: hidden; + position: absolute; + } + .lh-a11y-quick-focus.disabled { + visibility: hidden; + } + .lh-a11y-quick-focus:focus { + clip: auto; + height: auto; + width: auto; + z-index: 10000; /* lol */ + } + + +