diff --git a/core/audits/accessibility/axe-audit.js b/core/audits/accessibility/axe-audit.js index 6686878a6574..c80d3ced66dc 100644 --- a/core/audits/accessibility/axe-audit.js +++ b/core/audits/accessibility/axe-audit.js @@ -15,11 +15,17 @@ import * as i18n from '../../lib/i18n/i18n.js'; const UIStrings = { /** Label of a table column that identifies HTML elements that have failed an audit. */ failingElementsHeader: 'Failing Elements', + /** Label of a table column that identifies HTML elements that are related to a failure in an audit. */ + relatedElementsHeader: 'Related Elements', }; const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); class AxeAudit extends Audit { + static get relatedNodesLabel() { + return str_(UIStrings.relatedElementsHeader); + } + /** * Base class for audit rules which reflect assessment performed by the aXe accessibility library * See https://github.com/dequelabs/axe-core/blob/6b444546cff492a62a70a74a8fc3c62bd4729400/doc/API.md#results-object for result type and format details @@ -80,7 +86,10 @@ class AxeAudit extends Audit { }, subItems: axeNode.relatedNodes.length ? { type: 'subitems', - items: axeNode.relatedNodes.map(node => ({relatedNode: Audit.makeNodeItem(node)})), + label: this.relatedNodesLabel, + items: axeNode.relatedNodes.map(node => ({ + relatedNode: Audit.makeNodeItem(node), + })), } : undefined, })); } diff --git a/core/audits/accessibility/color-contrast.js b/core/audits/accessibility/color-contrast.js index 07ab6cd02d3c..a6d371694cbf 100644 --- a/core/audits/accessibility/color-contrast.js +++ b/core/audits/accessibility/color-contrast.js @@ -22,6 +22,8 @@ const UIStrings = { /** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ description: 'Low-contrast text is difficult or impossible for many users to read. ' + '[Learn how to provide sufficient color contrast](https://dequeuniversity.com/rules/axe/4.4/color-contrast).', + /** Label of a table column that identifies a single HTML element that is the background of another HTML element. */ + backgroundElementHeader: 'Background Element', }; const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); @@ -39,6 +41,10 @@ class ColorContrast extends AxeAudit { requiredArtifacts: ['Accessibility'], }; } + + static get relatedNodesLabel() { + return str_(UIStrings.backgroundElementHeader); + } } export default ColorContrast; diff --git a/core/audits/accessibility/duplicate-id-aria.js b/core/audits/accessibility/duplicate-id-aria.js index c91ad691e277..70dd35cf3076 100644 --- a/core/audits/accessibility/duplicate-id-aria.js +++ b/core/audits/accessibility/duplicate-id-aria.js @@ -19,6 +19,8 @@ const UIStrings = { failureTitle: 'ARIA IDs are not unique', /** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */ description: 'The value of an ARIA ID must be unique to prevent other instances from being overlooked by assistive technologies. [Learn how to fix duplicate ARIA IDs](https://dequeuniversity.com/rules/axe/4.4/duplicate-id-aria).', + /** Label of a table column that identifies HTML elements that are duplicates of another HTML element. */ + relatedElementsHeader: 'Duplicate Elements', }; const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings); @@ -36,6 +38,10 @@ class DuplicateIdAria extends AxeAudit { requiredArtifacts: ['Accessibility'], }; } + + static get relatedNodesLabel() { + return str_(UIStrings.relatedElementsHeader); + } } export default DuplicateIdAria; diff --git a/core/audits/byte-efficiency/byte-efficiency-audit.js b/core/audits/byte-efficiency/byte-efficiency-audit.js index 91df17ab5ae7..a2d9b83542d8 100644 --- a/core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/core/audits/byte-efficiency/byte-efficiency-audit.js @@ -26,6 +26,7 @@ const WASTED_MS_FOR_SCORE_OF_ZERO = 5000; * @property {Array} items * @property {Map=} wastedBytesByUrl * @property {LH.Audit.Details.Opportunity['headings']} headings + * @property {LH.Audit.Details.Table['subItemsLabel']=} subItemsLabel * @property {LH.IcuMessage} [displayValue] * @property {LH.IcuMessage} [explanation] * @property {Array} [warnings] @@ -229,6 +230,7 @@ class ByteEfficiencyAudit extends Audit { } const details = Audit.makeOpportunityDetails(result.headings, results, wastedMs, wastedBytes); + details.subItemsLabel = result.subItemsLabel; return { explanation: result.explanation, diff --git a/core/audits/byte-efficiency/duplicated-javascript.js b/core/audits/byte-efficiency/duplicated-javascript.js index 83af57a94376..0249fd76f9f7 100644 --- a/core/audits/byte-efficiency/duplicated-javascript.js +++ b/core/audits/byte-efficiency/duplicated-javascript.js @@ -238,6 +238,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit { return { items, headings, + subItemsLabel: str_(i18n.UIStrings.scriptResourceType), wastedBytesByUrl, }; } diff --git a/core/audits/byte-efficiency/legacy-javascript.js b/core/audits/byte-efficiency/legacy-javascript.js index a57ba0ac4688..bae161d29c15 100644 --- a/core/audits/byte-efficiency/legacy-javascript.js +++ b/core/audits/byte-efficiency/legacy-javascript.js @@ -467,6 +467,7 @@ class LegacyJavascript extends ByteEfficiencyAudit { return { items, headings, + subItemsLabel: str_(i18n.UIStrings.columnLocation), wastedBytesByUrl, }; } diff --git a/core/audits/byte-efficiency/unused-javascript.js b/core/audits/byte-efficiency/unused-javascript.js index d34a8827da61..91674610896a 100644 --- a/core/audits/byte-efficiency/unused-javascript.js +++ b/core/audits/byte-efficiency/unused-javascript.js @@ -155,6 +155,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit { {key: 'wastedBytes', valueType: 'bytes', subItemsHeading: {key: 'sourceWastedBytes'}, label: str_(i18n.UIStrings.columnWastedBytes)}, /* eslint-enable max-len */ ], + subItemsLabel: str_(i18n.UIStrings.module), }; } } diff --git a/core/audits/csp-xss.js b/core/audits/csp-xss.js index 265bf9f509ac..47a8c4cc5fee 100644 --- a/core/audits/csp-xss.js +++ b/core/audits/csp-xss.js @@ -173,6 +173,7 @@ class CspXss extends Audit { /* eslint-enable max-len */ ]; const details = Audit.makeTableDetails(headings, results); + details.subItemsLabel = str_(i18n.UIStrings.reasons); return { score, diff --git a/core/audits/dobetterweb/inspector-issues.js b/core/audits/dobetterweb/inspector-issues.js index ba16709f1201..b389f0cc4330 100644 --- a/core/audits/dobetterweb/inspector-issues.js +++ b/core/audits/dobetterweb/inspector-issues.js @@ -64,6 +64,7 @@ class IssuesPanelEntries extends Audit { issueType: 'Mixed content', subItems: { type: 'subitems', + label: str_(i18n.UIStrings.columnURL), items: Array.from(requestUrls).map(url => ({url})), }, }; @@ -85,6 +86,7 @@ class IssuesPanelEntries extends Audit { issueType: 'Cookie', subItems: { type: 'subitems', + label: str_(i18n.UIStrings.columnURL), items: Array.from(requestUrls).map(url => { return { url, @@ -110,6 +112,7 @@ class IssuesPanelEntries extends Audit { issueType: str_(UIStrings.issueTypeBlockedByResponse), subItems: { type: 'subitems', + label: str_(i18n.UIStrings.columnURL), items: Array.from(requestUrls).map(url => { return { url, @@ -135,6 +138,7 @@ class IssuesPanelEntries extends Audit { issueType: 'Content security policy', subItems: { type: 'subitems', + label: str_(i18n.UIStrings.columnURL), items: Array.from(requestUrls).map(url => { return { url, diff --git a/core/audits/non-composited-animations.js b/core/audits/non-composited-animations.js index 340bbfdecf69..c90c448dde2b 100644 --- a/core/audits/non-composited-animations.js +++ b/core/audits/non-composited-animations.js @@ -186,6 +186,7 @@ class NonCompositedAnimations extends Audit { } const details = Audit.makeTableDetails(headings, results); + details.subItemsLabel = str_(i18n.UIStrings.reasons); let displayValue; if (results.length > 0) { diff --git a/core/audits/seo/hreflang.js b/core/audits/seo/hreflang.js index 58f0efec6884..e49c50ecd108 100644 --- a/core/audits/seo/hreflang.js +++ b/core/audits/seo/hreflang.js @@ -136,6 +136,7 @@ class Hreflang extends Audit { }]; const details = Audit.makeTableDetails(headings, invalidHreflangs); + details.subItemsLabel = str_(i18n.UIStrings.reasons); return { score: Number(invalidHreflangs.length === 0), diff --git a/core/audits/third-party-facades.js b/core/audits/third-party-facades.js index 65131e88f417..1fe59f655a2b 100644 --- a/core/audits/third-party-facades.js +++ b/core/audits/third-party-facades.js @@ -187,7 +187,10 @@ class ThirdPartyFacades extends Audit { product: productWithCategory, transferSize: entitySummary.transferSize, blockingTime: entitySummary.blockingTime, - subItems: {type: 'subitems', items}, + subItems: { + type: 'subitems', + items, + }, }); } @@ -212,7 +215,10 @@ class ThirdPartyFacades extends Audit { displayValue: str_(UIStrings.displayValue, { itemCount: results.length, }), - details: Audit.makeTableDetails(headings, results), + details: { + ...Audit.makeTableDetails(headings, results), + subItemsLabel: str_(i18n.UIStrings.scriptResourceType), + }, }; } } diff --git a/core/lib/i18n/i18n.js b/core/lib/i18n/i18n.js index f15ef4733d87..7cc6722712c7 100644 --- a/core/lib/i18n/i18n.js +++ b/core/lib/i18n/i18n.js @@ -88,6 +88,10 @@ const UIStrings = { thirdPartyResourceType: 'Third-party', /** Label used to identify a value in a table where many individual values are aggregated to a single value, for brevity. "Other resources" could also be read as "the rest of the resources". Resource refers to network resources requested by the browser. */ otherResourcesLabel: 'Other resources', + /** Label for a row in a data table; 'Module' refers to a JavaScript module file. */ + module: 'Module', + /** Label for a row in a data table; What follows are reasons why something failed or occurred. */ + reasons: 'Reasons', /** The name of the metric that marks the time at which the first text or image is painted by the browser. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */ firstContentfulPaintMetric: 'First Contentful Paint', /** The name of the metric that marks the time at which the page is fully loaded and is able to quickly respond to user input (clicks, taps, and keypresses feel responsive). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */ diff --git a/core/test/audits/byte-efficiency/duplicated-javascript-test.js b/core/test/audits/byte-efficiency/duplicated-javascript-test.js index 1918a4fdf316..b68b238f9684 100644 --- a/core/test/audits/byte-efficiency/duplicated-javascript-test.js +++ b/core/test/audits/byte-efficiency/duplicated-javascript-test.js @@ -60,262 +60,262 @@ describe('DuplicatedJavascript computed artifact', () => { }; const networkRecords = [{url: 'https://example.com', resourceType: 'Document'}]; const results = await DuplicatedJavascript.audit_(artifacts, networkRecords, context); - expect({items: results.items, wastedBytesByUrl: results.wastedBytesByUrl}) - .toMatchInlineSnapshot(` - Object { + expect({items: results.items, wastedBytesByUrl: results.wastedBytesByUrl}). +toMatchInlineSnapshot(` +Object { + "items": Array [ + Object { + "source": "Control/assets/js/vendor/ng/select/select.js", + "subItems": Object { "items": Array [ Object { - "source": "Control/assets/js/vendor/ng/select/select.js", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 16009, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 16009, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 16009, + "sourceTransferBytes": 16009, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "Control/assets/js/vendor/ng/select/angular-sanitize.js", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 3015, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 3015, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 3015, + "sourceTransferBytes": 16009, + "url": "https://example.com/coursehero-bundle-2.js", }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 16009, + }, + Object { + "source": "Control/assets/js/vendor/ng/select/angular-sanitize.js", + "subItems": Object { + "items": Array [ Object { - "source": "node_modules/@babel/runtime", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 502, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 502, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 502, + "sourceTransferBytes": 3015, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "js/src/utils/service/amplitude-service.ts", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 445, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 437, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 437, + "sourceTransferBytes": 3015, + "url": "https://example.com/coursehero-bundle-2.js", }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 3015, + }, + Object { + "source": "node_modules/@babel/runtime", + "subItems": Object { + "items": Array [ Object { - "source": "js/src/search/results/store/filter-actions.ts", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 315, - "url": "https://example.com/coursehero-bundle-2.js", - }, - Object { - "sourceTransferBytes": 312, - "url": "https://example.com/coursehero-bundle-1.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 312, + "sourceTransferBytes": 502, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "js/src/search/results/store/item/resource-types.ts", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 258, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 256, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 256, + "sourceTransferBytes": 502, + "url": "https://example.com/coursehero-bundle-2.js", }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 502, + }, + Object { + "source": "js/src/utils/service/amplitude-service.ts", + "subItems": Object { + "items": Array [ Object { - "source": "js/src/search/results/store/filter-store.ts", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 4197, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 4175, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 4175, + "sourceTransferBytes": 445, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "js/src/search/results/view/filter/autocomplete-list.tsx", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 377, - "url": "https://example.com/coursehero-bundle-2.js", - }, - Object { - "sourceTransferBytes": 374, - "url": "https://example.com/coursehero-bundle-1.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 374, + "sourceTransferBytes": 437, + "url": "https://example.com/coursehero-bundle-2.js", }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 437, + }, + Object { + "source": "js/src/search/results/store/filter-actions.ts", + "subItems": Object { + "items": Array [ Object { - "source": "js/src/search/results/view/filter/autocomplete-filter.tsx", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 1262, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 1258, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 1258, + "sourceTransferBytes": 315, + "url": "https://example.com/coursehero-bundle-2.js", }, Object { - "source": "js/src/search/results/view/filter/autocomplete-filter-with-icon.tsx", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 890, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 889, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 889, + "sourceTransferBytes": 312, + "url": "https://example.com/coursehero-bundle-1.js", }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 312, + }, + Object { + "source": "js/src/search/results/store/item/resource-types.ts", + "subItems": Object { + "items": Array [ Object { - "source": "js/src/common/component/school-search.tsx", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 1927, - "url": "https://example.com/coursehero-bundle-2.js", - }, - Object { - "sourceTransferBytes": 1754, - "url": "https://example.com/coursehero-bundle-1.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 1754, + "sourceTransferBytes": 258, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "js/src/common/component/search/abstract-taxonomy-search.tsx", - "subItems": Object { - "items": Array [ - Object { - "sourceTransferBytes": 1024, - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "sourceTransferBytes": 1022, - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 1022, + "sourceTransferBytes": 256, + "url": "https://example.com/coursehero-bundle-2.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 256, + }, + Object { + "source": "js/src/search/results/store/filter-store.ts", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 4197, + "url": "https://example.com/coursehero-bundle-1.js", }, Object { - "source": "Other", - "subItems": Object { - "items": Array [ - Object { - "url": "https://example.com/coursehero-bundle-1.js", - }, - Object { - "url": "https://example.com/coursehero-bundle-2.js", - }, - ], - "type": "subitems", - }, - "totalBytes": 0, - "url": "", - "wastedBytes": 542, + "sourceTransferBytes": 4175, + "url": "https://example.com/coursehero-bundle-2.js", }, ], - "wastedBytesByUrl": Map { - "https://example.com/coursehero-bundle-2.js" => 27925, - "https://example.com/coursehero-bundle-1.js" => 2620, - }, - } - `); + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 4175, + }, + Object { + "source": "js/src/search/results/view/filter/autocomplete-list.tsx", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 377, + "url": "https://example.com/coursehero-bundle-2.js", + }, + Object { + "sourceTransferBytes": 374, + "url": "https://example.com/coursehero-bundle-1.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 374, + }, + Object { + "source": "js/src/search/results/view/filter/autocomplete-filter.tsx", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 1262, + "url": "https://example.com/coursehero-bundle-1.js", + }, + Object { + "sourceTransferBytes": 1258, + "url": "https://example.com/coursehero-bundle-2.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 1258, + }, + Object { + "source": "js/src/search/results/view/filter/autocomplete-filter-with-icon.tsx", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 890, + "url": "https://example.com/coursehero-bundle-1.js", + }, + Object { + "sourceTransferBytes": 889, + "url": "https://example.com/coursehero-bundle-2.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 889, + }, + Object { + "source": "js/src/common/component/school-search.tsx", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 1927, + "url": "https://example.com/coursehero-bundle-2.js", + }, + Object { + "sourceTransferBytes": 1754, + "url": "https://example.com/coursehero-bundle-1.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 1754, + }, + Object { + "source": "js/src/common/component/search/abstract-taxonomy-search.tsx", + "subItems": Object { + "items": Array [ + Object { + "sourceTransferBytes": 1024, + "url": "https://example.com/coursehero-bundle-1.js", + }, + Object { + "sourceTransferBytes": 1022, + "url": "https://example.com/coursehero-bundle-2.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 1022, + }, + Object { + "source": "Other", + "subItems": Object { + "items": Array [ + Object { + "url": "https://example.com/coursehero-bundle-1.js", + }, + Object { + "url": "https://example.com/coursehero-bundle-2.js", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "", + "wastedBytes": 542, + }, + ], + "wastedBytesByUrl": Map { + "https://example.com/coursehero-bundle-2.js" => 27925, + "https://example.com/coursehero-bundle-1.js" => 2620, + }, +} +`); }); it('.audit', async () => { diff --git a/core/test/audits/byte-efficiency/legacy-javascript-test.js b/core/test/audits/byte-efficiency/legacy-javascript-test.js index bcc44865b8b0..49df55450f23 100644 --- a/core/test/audits/byte-efficiency/legacy-javascript-test.js +++ b/core/test/audits/byte-efficiency/legacy-javascript-test.js @@ -96,28 +96,28 @@ describe('LegacyJavaScript audit', () => { ]); expect(result.items).toHaveLength(1); expect(result.items[0]).toMatchInlineSnapshot(` +Object { + "subItems": Object { + "items": Array [ Object { - "subItems": Object { - "items": Array [ - Object { - "location": Object { - "column": 0, - "line": 0, - "original": undefined, - "type": "source-location", - "url": "https://www.googletagmanager.com/a.js", - "urlProvider": "network", - }, - "signal": "String.prototype.repeat", - }, - ], - "type": "subitems", + "location": Object { + "column": 0, + "line": 0, + "original": undefined, + "type": "source-location", + "url": "https://www.googletagmanager.com/a.js", + "urlProvider": "network", }, - "totalBytes": 0, - "url": "https://www.googletagmanager.com/a.js", - "wastedBytes": 20104, - } - `); + "signal": "String.prototype.repeat", + }, + ], + "type": "subitems", + }, + "totalBytes": 0, + "url": "https://www.googletagmanager.com/a.js", + "wastedBytes": 20104, +} +`); expect(result.wastedBytesByUrl).toMatchInlineSnapshot(`Map {}`); }); diff --git a/core/test/audits/byte-efficiency/unused-javascript-test.js b/core/test/audits/byte-efficiency/unused-javascript-test.js index 9c2b81f13c0f..e5065eabd158 100644 --- a/core/test/audits/byte-efficiency/unused-javascript-test.js +++ b/core/test/audits/byte-efficiency/unused-javascript-test.js @@ -133,44 +133,44 @@ describe('UnusedJavaScript audit', () => { const result = await UnusedJavaScript.audit_(artifacts, networkRecords, context); expect(result.items).toMatchInlineSnapshot(` - Array [ +Array [ + Object { + "subItems": Object { + "items": Array [ Object { - "subItems": Object { - "items": Array [ - Object { - "source": "(unmapped)", - "sourceBytes": 10062, - "sourceWastedBytes": 3760, - }, - Object { - "source": "…src/codecs/webp/encoder-meta.ts", - "sourceBytes": 660, - "sourceWastedBytes": 660, - }, - Object { - "source": "…src/lib/util.ts", - "sourceBytes": 4043, - "sourceWastedBytes": 500, - }, - Object { - "source": "…src/custom-els/RangeInput/index.ts", - "sourceBytes": 2138, - "sourceWastedBytes": 293, - }, - Object { - "source": "…node_modules/comlink/comlink.js", - "sourceBytes": 4117, - "sourceWastedBytes": 256, - }, - ], - "type": "subitems", - }, - "totalBytes": 83748, - "url": "https://squoosh.app/main-app.js", - "wastedBytes": 6961, - "wastedPercent": 8.312435814764395, + "source": "(unmapped)", + "sourceBytes": 10062, + "sourceWastedBytes": 3760, }, - ] - `); + Object { + "source": "…src/codecs/webp/encoder-meta.ts", + "sourceBytes": 660, + "sourceWastedBytes": 660, + }, + Object { + "source": "…src/lib/util.ts", + "sourceBytes": 4043, + "sourceWastedBytes": 500, + }, + Object { + "source": "…src/custom-els/RangeInput/index.ts", + "sourceBytes": 2138, + "sourceWastedBytes": 293, + }, + Object { + "source": "…node_modules/comlink/comlink.js", + "sourceBytes": 4117, + "sourceWastedBytes": 256, + }, + ], + "type": "subitems", + }, + "totalBytes": 83748, + "url": "https://squoosh.app/main-app.js", + "wastedBytes": 6961, + "wastedPercent": 8.312435814764395, + }, +] +`); }); }); diff --git a/core/test/fixtures/fraggle-rock/reports/sample-flow-result.json b/core/test/fixtures/fraggle-rock/reports/sample-flow-result.json index b1380826b25d..177498e1cc1e 100644 --- a/core/test/fixtures/fraggle-rock/reports/sample-flow-result.json +++ b/core/test/fixtures/fraggle-rock/reports/sample-flow-result.json @@ -1730,7 +1730,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "unsized-images": { @@ -1894,7 +1895,8 @@ "severity": "High", "description": "No CSP found in enforcement mode" } - ] + ], + "subItemsLabel": "Reasons" } }, "full-page-screenshot": { @@ -3129,7 +3131,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Module" } }, "modern-image-formats": { @@ -3233,7 +3236,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Script" } }, "legacy-javascript": { @@ -3294,7 +3298,8 @@ } ], "overallSavingsMs": 0, - "overallSavingsBytes": 57 + "overallSavingsBytes": 57, + "subItemsLabel": "Location" } }, "doctype": { @@ -3659,7 +3664,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "plugins": { @@ -6478,7 +6484,8 @@ "audits[resource-summary].details.items[0].label" ], "core/lib/i18n/i18n.js | scriptResourceType": [ - "audits[resource-summary].details.items[1].label" + "audits[resource-summary].details.items[1].label", + "audits[duplicated-javascript].details.subItemsLabel" ], "core/lib/i18n/i18n.js | fontResourceType": [ "audits[resource-summary].details.items[2].label" @@ -6594,6 +6601,11 @@ "core/audits/non-composited-animations.js | description": [ "audits[non-composited-animations].description" ], + "core/lib/i18n/i18n.js | reasons": [ + "audits[non-composited-animations].details.subItemsLabel", + "audits[csp-xss].details.subItemsLabel", + "audits.hreflang.details.subItemsLabel" + ], "core/audits/unsized-images.js | failureTitle": [ "audits[unsized-images].title" ], @@ -6983,6 +6995,9 @@ "core/audits/byte-efficiency/unused-javascript.js | description": [ "audits[unused-javascript].description" ], + "core/lib/i18n/i18n.js | module": [ + "audits[unused-javascript].details.subItemsLabel" + ], "core/audits/byte-efficiency/modern-image-formats.js | title": [ "audits[modern-image-formats].title" ], @@ -7033,6 +7048,9 @@ "path": "audits[legacy-javascript].displayValue" } ], + "core/lib/i18n/i18n.js | columnLocation": [ + "audits[legacy-javascript].details.subItemsLabel" + ], "core/audits/dobetterweb/doctype.js | title": [ "audits.doctype.title" ], @@ -8583,7 +8601,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "unsized-images": { @@ -8940,7 +8959,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Module" } }, "modern-image-formats": { @@ -9044,7 +9064,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Script" } }, "legacy-javascript": { @@ -9061,7 +9082,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Location" } }, "inspector-issues": { @@ -10258,7 +10280,8 @@ "audits[resource-summary].details.items[2].label" ], "core/lib/i18n/i18n.js | scriptResourceType": [ - "audits[resource-summary].details.items[3].label" + "audits[resource-summary].details.items[3].label", + "audits[duplicated-javascript].details.subItemsLabel" ], "core/lib/i18n/i18n.js | stylesheetResourceType": [ "audits[resource-summary].details.items[4].label" @@ -10341,6 +10364,9 @@ "core/audits/non-composited-animations.js | description": [ "audits[non-composited-animations].description" ], + "core/lib/i18n/i18n.js | reasons": [ + "audits[non-composited-animations].details.subItemsLabel" + ], "core/audits/unsized-images.js | failureTitle": [ "audits[unsized-images].title" ], @@ -10408,6 +10434,9 @@ "core/audits/byte-efficiency/unused-javascript.js | description": [ "audits[unused-javascript].description" ], + "core/lib/i18n/i18n.js | module": [ + "audits[unused-javascript].details.subItemsLabel" + ], "core/audits/byte-efficiency/modern-image-formats.js | title": [ "audits[modern-image-formats].title" ], @@ -10450,6 +10479,9 @@ "core/audits/byte-efficiency/legacy-javascript.js | description": [ "audits[legacy-javascript].description" ], + "core/lib/i18n/i18n.js | columnLocation": [ + "audits[legacy-javascript].details.subItemsLabel" + ], "core/audits/dobetterweb/inspector-issues.js | title": [ "audits[inspector-issues].title" ], @@ -11340,6 +11372,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -11382,6 +11415,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -11424,6 +11458,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -11466,6 +11501,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -11508,6 +11544,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -11550,6 +11587,7 @@ }, "subItems": { "type": "subitems", + "label": "Background Element", "items": [ { "relatedNode": { @@ -13666,6 +13704,14 @@ "audits[color-contrast].details.headings[0].label", "audits[image-alt].details.headings[0].label" ], + "core/audits/accessibility/color-contrast.js | backgroundElementHeader": [ + "audits[color-contrast].details.items[0].subItems.label", + "audits[color-contrast].details.items[1].subItems.label", + "audits[color-contrast].details.items[2].subItems.label", + "audits[color-contrast].details.items[3].subItems.label", + "audits[color-contrast].details.items[4].subItems.label", + "audits[color-contrast].details.items[5].subItems.label" + ], "core/audits/accessibility/definition-list.js | title": [ "audits[definition-list].title" ], @@ -15716,7 +15762,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "unsized-images": { @@ -15899,7 +15946,8 @@ "severity": "High", "description": "No CSP found in enforcement mode" } - ] + ], + "subItemsLabel": "Reasons" } }, "full-page-screenshot": { @@ -17135,7 +17183,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Module" } }, "modern-image-formats": { @@ -17329,7 +17378,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Script" } }, "legacy-javascript": { @@ -17390,7 +17440,8 @@ } ], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Location" } }, "doctype": { @@ -17812,7 +17863,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "plugins": { @@ -20621,7 +20673,8 @@ "audits[resource-summary].details.items[2].label" ], "core/lib/i18n/i18n.js | scriptResourceType": [ - "audits[resource-summary].details.items[3].label" + "audits[resource-summary].details.items[3].label", + "audits[duplicated-javascript].details.subItemsLabel" ], "core/lib/i18n/i18n.js | fontResourceType": [ "audits[resource-summary].details.items[4].label" @@ -20721,6 +20774,11 @@ "core/audits/non-composited-animations.js | description": [ "audits[non-composited-animations].description" ], + "core/lib/i18n/i18n.js | reasons": [ + "audits[non-composited-animations].details.subItemsLabel", + "audits[csp-xss].details.subItemsLabel", + "audits.hreflang.details.subItemsLabel" + ], "core/audits/unsized-images.js | failureTitle": [ "audits[unsized-images].title" ], @@ -21115,6 +21173,9 @@ "core/audits/byte-efficiency/unused-javascript.js | description": [ "audits[unused-javascript].description" ], + "core/lib/i18n/i18n.js | module": [ + "audits[unused-javascript].details.subItemsLabel" + ], "core/audits/byte-efficiency/modern-image-formats.js | title": [ "audits[modern-image-formats].title" ], @@ -21175,6 +21236,9 @@ "core/audits/byte-efficiency/legacy-javascript.js | description": [ "audits[legacy-javascript].description" ], + "core/lib/i18n/i18n.js | columnLocation": [ + "audits[legacy-javascript].details.subItemsLabel" + ], "core/audits/dobetterweb/doctype.js | title": [ "audits.doctype.title" ], diff --git a/core/test/results/sample_v2.json b/core/test/results/sample_v2.json index dc2a30802aab..0f47e745049d 100644 --- a/core/test/results/sample_v2.json +++ b/core/test/results/sample_v2.json @@ -2504,7 +2504,8 @@ ] } } - ] + ], + "subItemsLabel": "Reasons" } }, "unsized-images": { @@ -2627,7 +2628,8 @@ "severity": "High", "description": "No CSP found in enforcement mode" } - ] + ], + "subItemsLabel": "Reasons" } }, "full-page-screenshot": { @@ -4727,7 +4729,8 @@ } ], "overallSavingsMs": 600, - "overallSavingsBytes": 65111 + "overallSavingsBytes": 65111, + "subItemsLabel": "Module" } }, "modern-image-formats": { @@ -5134,7 +5137,8 @@ "headings": [], "items": [], "overallSavingsMs": 0, - "overallSavingsBytes": 0 + "overallSavingsBytes": 0, + "subItemsLabel": "Script" } }, "legacy-javascript": { @@ -5205,7 +5209,8 @@ } ], "overallSavingsMs": 150, - "overallSavingsBytes": 21189 + "overallSavingsBytes": 21189, + "subItemsLabel": "Location" } }, "doctype": { @@ -5803,7 +5808,8 @@ "details": { "type": "table", "headings": [], - "items": [] + "items": [], + "subItemsLabel": "Reasons" } }, "plugins": { @@ -8838,7 +8844,8 @@ ], "core/lib/i18n/i18n.js | scriptResourceType": [ "audits[performance-budget].details.items[2].label", - "audits[resource-summary].details.items[2].label" + "audits[resource-summary].details.items[2].label", + "audits[duplicated-javascript].details.subItemsLabel" ], "core/lib/i18n/i18n.js | otherResourceType": [ "audits[performance-budget].details.items[3].label", @@ -9001,6 +9008,11 @@ "path": "audits[non-composited-animations].details.items[0].subItems.items[0].failureReason" } ], + "core/lib/i18n/i18n.js | reasons": [ + "audits[non-composited-animations].details.subItemsLabel", + "audits[csp-xss].details.subItemsLabel", + "audits.hreflang.details.subItemsLabel" + ], "core/audits/unsized-images.js | failureTitle": [ "audits[unsized-images].title" ], @@ -9456,6 +9468,9 @@ "core/audits/byte-efficiency/unused-javascript.js | description": [ "audits[unused-javascript].description" ], + "core/lib/i18n/i18n.js | module": [ + "audits[unused-javascript].details.subItemsLabel" + ], "core/audits/byte-efficiency/modern-image-formats.js | title": [ "audits[modern-image-formats].title" ], @@ -9503,6 +9518,9 @@ "core/audits/byte-efficiency/legacy-javascript.js | description": [ "audits[legacy-javascript].description" ], + "core/lib/i18n/i18n.js | columnLocation": [ + "audits[legacy-javascript].details.subItemsLabel" + ], "core/audits/dobetterweb/doctype.js | title": [ "audits.doctype.title" ], diff --git a/report/assets/styles.css b/report/assets/styles.css index ab891bef1061..b0d7f6d8fbd7 100644 --- a/report/assets/styles.css +++ b/report/assets/styles.css @@ -1515,6 +1515,10 @@ word-break: normal; } +.lh-table hr { + border-width: 0.5px; +} + .lh-row--even { background-color: var(--table-higlight-background-color); } diff --git a/report/renderer/components.js b/report/renderer/components.js index cd4b97f4df95..25d7243b7b48 100644 --- a/report/renderer/components.js +++ b/report/renderer/components.js @@ -520,7 +520,7 @@ function createSnippetLineComponent(dom) { function createStylesComponent(dom) { const el0 = dom.createFragment(); const el1 = dom.createElement("style"); - el1.append("/**\n * @license\n * Copyright 2017 The Lighthouse Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS-IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Naming convention:\n\n If a variable is used for a specific component: --{component}-{property name}-{modifier}\n\n Both {component} and {property name} should be kebab-case. If the target is the entire page,\n use 'report' for the component. The property name should not be abbreviated. Use the\n property name the variable is intended for - if it's used for multiple, a common descriptor\n is fine (ex: 'size' for a variable applied to 'width' and 'height'). If a variable is shared\n across multiple components, either create more variables or just drop the \"{component}-\"\n part of the name. Append any modifiers at the end (ex: 'big', 'dark').\n\n For colors: --color-{hue}-{intensity}\n\n {intensity} is the Material Design tag - 700, A700, etc.\n*/\n.lh-vars {\n /* Palette using Material Design Colors\n * https://www.materialui.co/colors */\n --color-amber-50: #FFF8E1;\n --color-blue-200: #90CAF9;\n --color-blue-900: #0D47A1;\n --color-blue-A700: #2962FF;\n --color-blue-primary: #06f;\n --color-cyan-500: #00BCD4;\n --color-gray-100: #F5F5F5;\n --color-gray-300: #CFCFCF;\n --color-gray-200: #E0E0E0;\n --color-gray-400: #BDBDBD;\n --color-gray-50: #FAFAFA;\n --color-gray-500: #9E9E9E;\n --color-gray-600: #757575;\n --color-gray-700: #616161;\n --color-gray-800: #424242;\n --color-gray-900: #212121;\n --color-gray: #000000;\n --color-green-700: #080;\n --color-green: #0c6;\n --color-lime-400: #D3E156;\n --color-orange-50: #FFF3E0;\n --color-orange-700: #C33300;\n --color-orange: #fa3;\n --color-red-700: #c00;\n --color-red: #f33;\n --color-teal-600: #00897B;\n --color-white: #FFFFFF;\n\n /* Context-specific colors */\n --color-average-secondary: var(--color-orange-700);\n --color-average: var(--color-orange);\n --color-fail-secondary: var(--color-red-700);\n --color-fail: var(--color-red);\n --color-hover: var(--color-gray-50);\n --color-informative: var(--color-blue-900);\n --color-pass-secondary: var(--color-green-700);\n --color-pass: var(--color-green);\n --color-not-applicable: var(--color-gray-600);\n\n /* Component variables */\n --audit-description-padding-left: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right));\n --audit-explanation-line-height: 16px;\n --audit-group-margin-bottom: calc(var(--default-padding) * 6);\n --audit-group-padding-vertical: 8px;\n --audit-margin-horizontal: 5px;\n --audit-padding-vertical: 8px;\n --category-padding: calc(var(--default-padding) * 6) var(--edge-gap-padding) calc(var(--default-padding) * 4);\n --chevron-line-stroke: var(--color-gray-600);\n --chevron-size: 12px;\n --default-padding: 8px;\n --edge-gap-padding: calc(var(--default-padding) * 4);\n --env-item-background-color: var(--color-gray-100);\n --env-item-font-size: 28px;\n --env-item-line-height: 36px;\n --env-item-padding: 10px 0px;\n --env-name-min-width: 220px;\n --footer-padding-vertical: 16px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 48px;\n --gauge-circle-size-sm: 32px;\n --gauge-label-font-size-big: 18px;\n --gauge-label-font-size: var(--report-font-size-secondary);\n --gauge-label-line-height-big: 24px;\n --gauge-label-line-height: var(--report-line-height-secondary);\n --gauge-percentage-font-size-big: 38px;\n --gauge-percentage-font-size: var(--report-font-size-secondary);\n --gauge-wrapper-width: 120px;\n --header-line-height: 24px;\n --highlighter-background-color: var(--report-text-color);\n --icon-square-size: calc(var(--score-icon-size) * 0.88);\n --image-preview-size: 48px;\n --link-color: var(--color-blue-primary);\n --locale-selector-background-color: var(--color-white);\n --metric-toggle-lines-fill: #7F7F7F;\n --metric-value-font-size: calc(var(--report-font-size) * 1.8);\n --metrics-toggle-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-white);\n --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);\n --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);\n --plugin-icon-size: 65%;\n --pwa-icon-margin: 0 var(--default-padding);\n --pwa-icon-size: var(--topbar-logo-size);\n --report-background-color: #fff;\n --report-border-color-secondary: #ebebeb;\n --report-font-family-monospace: 'Roboto Mono', 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: Roboto, Helvetica, Arial, sans-serif;\n --report-font-size: 14px;\n --report-font-size-secondary: 12px;\n --report-icon-size: var(--score-icon-background-size);\n --report-line-height: 24px;\n --report-line-height-secondary: 20px;\n --report-monospace-font-size: calc(var(--report-font-size) * 0.85);\n --report-text-color-secondary: var(--color-gray-800);\n --report-text-color: var(--color-gray-900);\n --report-content-max-width: calc(60 * var(--report-font-size)); /* defaults to 840px */\n --report-content-min-width: 360px;\n --report-content-max-width-minus-edge-gap: calc(var(--report-content-max-width) - var(--edge-gap-padding) * 2);\n --score-container-padding: 8px;\n --score-icon-background-size: 24px;\n --score-icon-margin-left: 6px;\n --score-icon-margin-right: 14px;\n --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);\n --score-icon-size: 12px;\n --score-icon-size-big: 16px;\n --screenshot-overlay-background: rgba(0, 0, 0, 0.3);\n --section-padding-vertical: calc(var(--default-padding) * 6);\n --snippet-background-color: var(--color-gray-50);\n --snippet-color: #0938C2;\n --sparkline-height: 5px;\n --stackpack-padding-horizontal: 10px;\n --sticky-header-background-color: var(--report-background-color);\n --table-higlight-background-color: hsla(210, 17%, 77%, 0.1);\n --tools-icon-color: var(--color-gray-600);\n --topbar-background-color: var(--color-white);\n --topbar-height: 32px;\n --topbar-logo-size: 24px;\n --topbar-padding: 0 8px;\n --toplevel-warning-background-color: hsla(30, 100%, 75%, 10%);\n --toplevel-warning-message-text-color: var(--color-average-secondary);\n --toplevel-warning-padding: 18px;\n --toplevel-warning-text-color: var(--report-text-color);\n\n /* SVGs */\n --plugin-icon-url-dark: url('data:image/svg+xml;utf8,');\n --plugin-icon-url: url('data:image/svg+xml;utf8,');\n\n --pass-icon-url: url('data:image/svg+xml;utf8,check');\n --average-icon-url: url('data:image/svg+xml;utf8,info');\n --fail-icon-url: url('data:image/svg+xml;utf8,warn');\n\n --pwa-installable-gray-url: url('data:image/svg+xml;utf8,');\n --pwa-optimized-gray-url: url('data:image/svg+xml;utf8,');\n\n --pwa-installable-gray-url-dark: url('data:image/svg+xml;utf8,');\n --pwa-optimized-gray-url-dark: url('data:image/svg+xml;utf8,');\n\n --pwa-installable-color-url: url('data:image/svg+xml;utf8,');\n --pwa-optimized-color-url: url('data:image/svg+xml;utf8,');\n\n --swap-locale-icon-url: url('data:image/svg+xml;utf8,');\n}\n\n@media not print {\n .lh-dark {\n /* Pallete */\n --color-gray-200: var(--color-gray-800);\n --color-gray-300: #616161;\n --color-gray-400: var(--color-gray-600);\n --color-gray-700: var(--color-gray-400);\n --color-gray-50: #757575;\n --color-gray-600: var(--color-gray-500);\n --color-green-700: var(--color-green);\n --color-orange-700: var(--color-orange);\n --color-red-700: var(--color-red);\n --color-teal-600: var(--color-cyan-500);\n\n /* Context-specific colors */\n --color-hover: rgba(0, 0, 0, 0.2);\n --color-informative: var(--color-blue-200);\n\n /* Component variables */\n --env-item-background-color: #393535;\n --link-color: var(--color-blue-200);\n --locale-selector-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-gray-800);\n --report-background-color: var(--color-gray-900);\n --report-border-color-secondary: var(--color-gray-200);\n --report-text-color-secondary: var(--color-gray-400);\n --report-text-color: var(--color-gray-100);\n --snippet-color: var(--color-cyan-500);\n --topbar-background-color: var(--color-gray);\n --toplevel-warning-background-color: hsl(33deg 14% 18%);\n --toplevel-warning-message-text-color: var(--color-orange-700);\n --toplevel-warning-text-color: var(--color-gray-100);\n\n /* SVGs */\n --plugin-icon-url: var(--plugin-icon-url-dark);\n --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark);\n --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark);\n }\n}\n\n@media only screen and (max-width: 480px) {\n .lh-vars {\n --audit-group-margin-bottom: 20px;\n --edge-gap-padding: var(--default-padding);\n --env-name-min-width: 120px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 72px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 112px;\n --header-padding: 16px 0 16px 0;\n --image-preview-size: 24px;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-size: 14px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --topbar-height: 28px;\n --topbar-logo-size: 20px;\n }\n\n /* Not enough space to adequately show the relative savings bars. */\n .lh-sparkline {\n display: none;\n }\n}\n\n.lh-vars.lh-devtools {\n --audit-explanation-line-height: 14px;\n --audit-group-margin-bottom: 20px;\n --audit-group-padding-vertical: 12px;\n --audit-padding-vertical: 4px;\n --category-padding: 12px;\n --default-padding: 12px;\n --env-name-min-width: 120px;\n --footer-padding-vertical: 8px;\n --gauge-circle-size-big: 72px;\n --gauge-circle-size: 64px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 97px;\n --header-line-height: 20px;\n --header-padding: 16px 0 16px 0;\n --screenshot-overlay-background: transparent;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-family-monospace: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif;\n --report-font-size: 12px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --section-padding-vertical: 8px;\n}\n\n.lh-devtools.lh-root {\n height: 100%;\n}\n.lh-devtools.lh-root img {\n /* Override devtools default 'min-width: 0' so svg without size in a flexbox isn't collapsed. */\n min-width: auto;\n}\n.lh-devtools .lh-container {\n overflow-y: scroll;\n height: calc(100% - var(--topbar-height));\n}\n@media print {\n .lh-devtools .lh-container {\n overflow: unset;\n }\n}\n.lh-devtools .lh-sticky-header {\n /* This is normally the height of the topbar, but we want it to stick to the top of our scroll container .lh-container` */\n top: 0;\n}\n.lh-devtools .lh-element-screenshot__overlay {\n position: absolute;\n}\n\n@keyframes fadeIn {\n 0% { opacity: 0;}\n 100% { opacity: 0.6;}\n}\n\n.lh-root *, .lh-root *::before, .lh-root *::after {\n box-sizing: border-box;\n}\n\n.lh-root {\n font-family: var(--report-font-family);\n font-size: var(--report-font-size);\n margin: 0;\n line-height: var(--report-line-height);\n background: var(--report-background-color);\n color: var(--report-text-color);\n}\n\n.lh-root :focus-visible {\n outline: -webkit-focus-ring-color auto 3px;\n}\n.lh-root summary:focus {\n outline: none;\n box-shadow: 0 0 0 1px hsl(217, 89%, 61%);\n}\n\n.lh-root [hidden] {\n display: none !important;\n}\n\n.lh-root pre {\n margin: 0;\n}\n\n.lh-root details > summary {\n cursor: pointer;\n}\n\n.lh-hidden {\n display: none !important;\n}\n\n.lh-container {\n /*\n Text wrapping in the report is so much FUN!\n We have a `word-break: break-word;` globally here to prevent a few common scenarios, namely\n long non-breakable text (usually URLs) found in:\n 1. The footer\n 2. .lh-node (outerHTML)\n 3. .lh-code\n\n With that sorted, the next challenge is appropriate column sizing and text wrapping inside our\n .lh-details tables. Even more fun.\n * We don't want table headers (\"Potential Savings (ms)\") to wrap or their column values, but\n we'd be happy for the URL column to wrap if the URLs are particularly long.\n * We want the narrow columns to remain narrow, providing the most column width for URL\n * We don't want the table to extend past 100% width.\n * Long URLs in the URL column can wrap. Util.getURLDisplayName maxes them out at 64 characters,\n but they do not get any overflow:ellipsis treatment.\n */\n word-break: break-word;\n}\n\n.lh-audit-group a,\n.lh-category-header__description a,\n.lh-audit__description a,\n.lh-warnings a,\n.lh-footer a,\n.lh-table-column--link a {\n color: var(--link-color);\n}\n\n.lh-audit__description, .lh-audit__stackpack {\n --inner-audit-padding-right: var(--stackpack-padding-horizontal);\n padding-left: var(--audit-description-padding-left);\n padding-right: var(--inner-audit-padding-right);\n padding-top: 8px;\n padding-bottom: 8px;\n}\n\n.lh-details {\n margin-top: var(--default-padding);\n margin-bottom: var(--default-padding);\n margin-left: var(--audit-description-padding-left);\n /* whatever the .lh-details side margins are */\n width: 100%;\n}\n\n.lh-audit__stackpack {\n display: flex;\n align-items: center;\n}\n\n.lh-audit__stackpack__img {\n max-width: 30px;\n margin-right: var(--default-padding)\n}\n\n/* Report header */\n\n.lh-report-icon {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n cursor: pointer;\n}\n.lh-report-icon[disabled] {\n opacity: 0.3;\n pointer-events: none;\n}\n\n.lh-report-icon::before {\n content: \"\";\n margin: 4px;\n background-repeat: no-repeat;\n width: var(--report-icon-size);\n height: var(--report-icon-size);\n opacity: 0.7;\n display: inline-block;\n vertical-align: middle;\n}\n.lh-report-icon:hover::before {\n opacity: 1;\n}\n.lh-dark .lh-report-icon::before {\n filter: invert(1);\n}\n.lh-report-icon--print::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--copy::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--open::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--download::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--dark::before {\n background-image:url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--treemap::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--date::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--devices::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--world::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--stopwatch::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--networkspeed::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--samples-one::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--samples-many::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--chrome::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n\n\n\n.lh-buttons {\n display: flex;\n flex-wrap: wrap;\n margin: var(--default-padding) 0;\n}\n.lh-button {\n height: 32px;\n border: 1px solid var(--report-border-color-secondary);\n border-radius: 3px;\n color: var(--link-color);\n background-color: var(--report-background-color);\n margin: 5px;\n}\n\n.lh-button:first-of-type {\n margin-left: 0;\n}\n\n/* Node */\n.lh-node__snippet {\n font-family: var(--report-font-family-monospace);\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n line-height: 20px;\n}\n\n/* Score */\n\n.lh-audit__score-icon {\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n margin: var(--score-icon-margin);\n}\n\n.lh-audit--pass .lh-audit__display-text {\n color: var(--color-pass-secondary);\n}\n.lh-audit--pass .lh-audit__score-icon,\n.lh-scorescale-range--pass::before {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-audit__display-text {\n color: var(--color-average-secondary);\n}\n.lh-audit--average .lh-audit__score-icon,\n.lh-scorescale-range--average::before {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-audit--fail .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n.lh-audit--fail .lh-audit__score-icon,\n.lh-audit--error .lh-audit__score-icon,\n.lh-scorescale-range--fail::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-audit--manual .lh-audit__display-text,\n.lh-audit--notapplicable .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n.lh-audit--manual .lh-audit__score-icon,\n.lh-audit--notapplicable .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n background: none;\n}\n\n.lh-audit--informative .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n\n.lh-audit--informative .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n}\n\n.lh-audit__description,\n.lh-audit__stackpack {\n color: var(--report-text-color-secondary);\n}\n.lh-audit__adorn {\n border: 1px solid slategray;\n border-radius: 3px;\n margin: 0 3px;\n padding: 0 2px;\n line-height: 1.1;\n display: inline-block;\n font-size: 90%;\n}\n\n.lh-category-header__description {\n text-align: center;\n color: var(--color-gray-700);\n margin: 0px auto;\n max-width: 400px;\n}\n\n\n.lh-audit__display-text,\n.lh-load-opportunity__sparkline,\n.lh-chevron-container {\n margin: 0 var(--audit-margin-horizontal);\n}\n.lh-chevron-container {\n margin-right: 0;\n}\n\n.lh-audit__title-and-text {\n flex: 1;\n}\n\n.lh-audit__title-and-text code {\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n}\n\n/* Prepend display text with em dash separator. But not in Opportunities. */\n.lh-audit__display-text:not(:empty):before {\n content: '—';\n margin-right: var(--audit-margin-horizontal);\n}\n.lh-audit-group.lh-audit-group--load-opportunities .lh-audit__display-text:not(:empty):before {\n display: none;\n}\n\n/* Expandable Details (Audit Groups, Audits) */\n.lh-audit__header {\n display: flex;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-audit--load-opportunity .lh-audit__header {\n display: block;\n}\n\n\n.lh-metricfilter {\n display: grid;\n justify-content: end;\n align-items: center;\n grid-auto-flow: column;\n gap: 4px;\n color: var(--color-gray-700);\n}\n\n.lh-metricfilter__radio {\n position: absolute;\n left: -9999px;\n}\n.lh-metricfilter input[type='radio']:focus-visible + label {\n outline: -webkit-focus-ring-color auto 1px;\n}\n\n.lh-metricfilter__label {\n display: inline-flex;\n padding: 0 4px;\n height: 16px;\n text-decoration: underline;\n align-items: center;\n cursor: pointer;\n font-size: 90%;\n}\n\n.lh-metricfilter__label--active {\n background: var(--color-blue-primary);\n color: var(--color-white);\n border-radius: 3px;\n text-decoration: none;\n}\n/* Give the 'All' choice a more muted display */\n.lh-metricfilter__label--active[for=\"metric-All\"] {\n background-color: var(--color-blue-200) !important;\n color: black !important;\n}\n\n.lh-metricfilter__text {\n margin-right: 8px;\n}\n\n/* If audits are filtered, hide the itemcount for Passed Audits… */\n.lh-category--filtered .lh-audit-group .lh-audit-group__itemcount {\n display: none;\n}\n\n\n.lh-audit__header:hover {\n background-color: var(--color-hover);\n}\n\n/* We want to hide the browser's default arrow marker on summary elements. Admittedly, it's complicated. */\n.lh-root details > summary {\n /* Blink 89+ and Firefox will hide the arrow when display is changed from (new) default of `list-item` to block. https://chromestatus.com/feature/6730096436051968*/\n display: block;\n}\n/* Safari and Blink <=88 require using the -webkit-details-marker selector */\n.lh-root details > summary::-webkit-details-marker {\n display: none;\n}\n\n/* Perf Metric */\n\n.lh-metrics-container {\n display: grid;\n grid-auto-rows: 1fr;\n grid-template-columns: 1fr 1fr;\n grid-column-gap: var(--report-line-height);\n margin-bottom: var(--default-padding);\n}\n\n.lh-metric {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(-n+2) {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-metric__innerwrap {\n display: grid;\n /**\n * Icon -- Metric Name\n * -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 1fr;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-metric__details {\n order: -1;\n}\n\n.lh-metric__title {\n flex: 1;\n}\n\n.lh-calclink {\n padding-left: calc(1ex / 3);\n}\n\n.lh-metric__description {\n display: none;\n grid-column-start: 2;\n grid-column-end: 4;\n color: var(--report-text-color-secondary);\n}\n\n.lh-metric__value {\n font-size: var(--metric-value-font-size);\n margin: calc(var(--default-padding) / 2) 0;\n white-space: nowrap; /* No wrapping between metric value and the icon */\n grid-column-start: 2;\n}\n\n\n@media screen and (max-width: 535px) {\n .lh-metrics-container {\n display: block;\n }\n\n .lh-metric {\n border-bottom: none !important;\n }\n .lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(1) {\n border-bottom: 1px solid var(--report-border-color-secondary) !important;\n }\n\n /* Change the grid to 3 columns for narrow viewport. */\n .lh-metric__innerwrap {\n /**\n * Icon -- Metric Name -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 2fr 1fr;\n }\n .lh-metric__value {\n justify-self: end;\n grid-column-start: unset;\n }\n}\n\n/* No-JS toggle switch */\n/* Keep this selector sync'd w/ `magicSelector` in report-ui-features-test.js */\n .lh-metrics-toggle__input:checked ~ .lh-metrics-container .lh-metric__description {\n display: block;\n}\n\n/* TODO get rid of the SVGS and clean up these some more */\n.lh-metrics-toggle__input {\n opacity: 0;\n position: absolute;\n right: 0;\n top: 0px;\n}\n\n.lh-metrics-toggle__input + div > label > .lh-metrics-toggle__labeltext--hide,\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--show {\n display: none;\n}\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--hide {\n display: inline;\n}\n.lh-metrics-toggle__input:focus + div > label {\n outline: -webkit-focus-ring-color auto 3px;\n}\n\n.lh-metrics-toggle__label {\n cursor: pointer;\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n/* Pushes the metric description toggle button to the right. */\n.lh-audit-group--metrics .lh-audit-group__header {\n display: flex;\n justify-content: space-between;\n}\n\n.lh-metric__icon,\n.lh-scorescale-range::before {\n content: '';\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n display: inline-block;\n margin: var(--score-icon-margin);\n}\n\n.lh-metric--pass .lh-metric__value {\n color: var(--color-pass-secondary);\n}\n.lh-metric--pass .lh-metric__icon {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-metric--average .lh-metric__value {\n color: var(--color-average-secondary);\n}\n.lh-metric--average .lh-metric__icon {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-metric--fail .lh-metric__value {\n color: var(--color-fail-secondary);\n}\n.lh-metric--fail .lh-metric__icon,\n.lh-metric--error .lh-metric__icon {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-metric--error .lh-metric__value,\n.lh-metric--error .lh-metric__description {\n color: var(--color-fail-secondary);\n}\n\n/* Perf load opportunity */\n\n.lh-load-opportunity__cols {\n display: flex;\n align-items: flex-start;\n}\n\n.lh-load-opportunity__header .lh-load-opportunity__col {\n color: var(--color-gray-600);\n display: unset;\n line-height: calc(2.3 * var(--report-font-size));\n}\n\n.lh-load-opportunity__col {\n display: flex;\n}\n\n.lh-load-opportunity__col--one {\n flex: 5;\n align-items: center;\n margin-right: 2px;\n}\n.lh-load-opportunity__col--two {\n flex: 4;\n text-align: right;\n}\n\n.lh-audit--load-opportunity .lh-audit__display-text {\n text-align: right;\n flex: 0 0 calc(4 * var(--report-font-size));\n}\n\n\n/* Sparkline */\n\n.lh-load-opportunity__sparkline {\n flex: 1;\n margin-top: calc((var(--report-line-height) - var(--sparkline-height)) / 2);\n}\n\n.lh-sparkline {\n height: var(--sparkline-height);\n width: 100%;\n}\n\n.lh-sparkline__bar {\n height: 100%;\n float: right;\n}\n\n.lh-audit--pass .lh-sparkline__bar {\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-sparkline__bar {\n background: var(--color-average);\n}\n\n.lh-audit--fail .lh-sparkline__bar {\n background: var(--color-fail);\n}\n\n/* Filmstrip */\n\n.lh-filmstrip-container {\n /* smaller gap between metrics and filmstrip */\n margin: -8px auto 0 auto;\n}\n\n.lh-filmstrip {\n display: grid;\n justify-content: space-between;\n padding-bottom: var(--default-padding);\n width: 100%;\n grid-template-columns: repeat(auto-fit, 60px);\n}\n\n.lh-filmstrip__frame {\n text-align: right;\n position: relative;\n}\n\n.lh-filmstrip__thumbnail {\n border: 1px solid var(--report-border-color-secondary);\n max-height: 100px;\n max-width: 60px;\n}\n\n/* Audit */\n\n.lh-audit {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n/* Apply border-top to just the first audit. */\n.lh-audit {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n.lh-audit ~ .lh-audit {\n border-top: none;\n}\n\n\n.lh-audit--error .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n\n/* Audit Group */\n\n.lh-audit-group {\n margin-bottom: var(--audit-group-margin-bottom);\n position: relative;\n}\n.lh-audit-group--metrics {\n margin-bottom: calc(var(--audit-group-margin-bottom) / 2);\n}\n\n.lh-audit-group__header::before {\n /* By default, groups don't get an icon */\n content: none;\n width: var(--pwa-icon-size);\n height: var(--pwa-icon-size);\n margin: var(--pwa-icon-margin);\n display: inline-block;\n vertical-align: middle;\n}\n\n/* Style the \"over budget\" columns red. */\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(4),\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(5),\n.lh-audit-group--budgets #timing-budget tbody tr td:nth-child(3) {\n color: var(--color-red-700);\n}\n\n/* Align the \"over budget request count\" text to be close to the \"over budget bytes\" column. */\n.lh-audit-group--budgets .lh-table tbody tr td:nth-child(4){\n text-align: right;\n}\n\n.lh-audit-group--budgets .lh-details--budget {\n width: 100%;\n margin: 0 0 var(--default-padding);\n}\n\n.lh-audit-group--pwa-installable .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-installable-gray-url);\n}\n.lh-audit-group--pwa-optimized .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-optimized-gray-url);\n}\n.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-installable-color-url);\n}\n.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-optimized-color-url);\n}\n\n.lh-audit-group--metrics .lh-audit-group__summary {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.lh-audit-group__summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.lh-audit-group__header .lh-chevron {\n margin-top: calc((var(--report-line-height) - 5px) / 2);\n}\n\n.lh-audit-group__header {\n letter-spacing: 0.8px;\n padding: var(--default-padding);\n padding-left: 0;\n}\n\n.lh-audit-group__header, .lh-audit-group__summary {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n.lh-audit-group__title {\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.lh-audit-group__itemcount {\n color: var(--color-gray-600);\n}\n\n.lh-audit-group__footer {\n color: var(--color-gray-600);\n display: block;\n margin-top: var(--default-padding);\n}\n\n.lh-details,\n.lh-category-header__description,\n.lh-load-opportunity__header,\n.lh-audit-group__footer {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n}\n\n.lh-audit-explanation {\n margin: var(--audit-padding-vertical) 0 calc(var(--audit-padding-vertical) / 2) var(--audit-margin-horizontal);\n line-height: var(--audit-explanation-line-height);\n display: inline-block;\n}\n\n.lh-audit--fail .lh-audit-explanation {\n color: var(--color-fail-secondary);\n}\n\n/* Report */\n.lh-list > :not(:last-child) {\n margin-bottom: calc(var(--default-padding) * 2);\n}\n\n.lh-header-container {\n display: block;\n margin: 0 auto;\n position: relative;\n word-wrap: break-word;\n}\n\n.lh-header-container .lh-scores-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n\n\n.lh-report {\n min-width: var(--report-content-min-width);\n}\n\n.lh-exception {\n font-size: large;\n}\n\n.lh-code {\n white-space: normal;\n margin-top: 0;\n font-size: var(--report-monospace-font-size);\n}\n\n.lh-warnings {\n --item-margin: calc(var(--report-line-height) / 6);\n color: var(--color-average-secondary);\n margin: var(--audit-padding-vertical) 0;\n padding: var(--default-padding)\n var(--default-padding)\n var(--default-padding)\n calc(var(--audit-description-padding-left));\n background-color: var(--toplevel-warning-background-color);\n}\n.lh-warnings span {\n font-weight: bold;\n}\n\n.lh-warnings--toplevel {\n --item-margin: calc(var(--header-line-height) / 4);\n color: var(--toplevel-warning-text-color);\n margin-left: auto;\n margin-right: auto;\n max-width: var(--report-content-max-width-minus-edge-gap);\n padding: var(--toplevel-warning-padding);\n border-radius: 8px;\n}\n\n.lh-warnings__msg {\n color: var(--toplevel-warning-message-text-color);\n margin: 0;\n}\n\n.lh-warnings ul {\n margin: 0;\n}\n.lh-warnings li {\n margin: var(--item-margin) 0;\n}\n.lh-warnings li:last-of-type {\n margin-bottom: 0;\n}\n\n.lh-scores-header {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n}\n.lh-scores-header__solo {\n padding: 0;\n border: 0;\n}\n\n/* Gauge */\n\n.lh-gauge__wrapper--pass {\n color: var(--color-pass-secondary);\n fill: var(--color-pass);\n stroke: var(--color-pass);\n}\n\n.lh-gauge__wrapper--average {\n color: var(--color-average-secondary);\n fill: var(--color-average);\n stroke: var(--color-average);\n}\n\n.lh-gauge__wrapper--fail {\n color: var(--color-fail-secondary);\n fill: var(--color-fail);\n stroke: var(--color-fail);\n}\n\n.lh-gauge__wrapper--not-applicable {\n color: var(--color-not-applicable);\n fill: var(--color-not-applicable);\n stroke: var(--color-not-applicable);\n}\n\n.lh-fraction__wrapper .lh-fraction__content::before {\n content: '';\n height: var(--score-icon-size);\n width: var(--score-icon-size);\n margin: var(--score-icon-margin);\n display: inline-block;\n}\n.lh-fraction__wrapper--pass .lh-fraction__content {\n color: var(--color-pass-secondary);\n}\n.lh-fraction__wrapper--pass .lh-fraction__background {\n background-color: var(--color-pass);\n}\n.lh-fraction__wrapper--pass .lh-fraction__content::before {\n background-color: var(--color-pass);\n border-radius: 50%;\n}\n.lh-fraction__wrapper--average .lh-fraction__content {\n color: var(--color-average-secondary);\n}\n.lh-fraction__wrapper--average .lh-fraction__background,\n.lh-fraction__wrapper--average .lh-fraction__content::before {\n background-color: var(--color-average);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content {\n color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__background {\n background-color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n.lh-fraction__wrapper--null .lh-fraction__content {\n color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__background {\n background-color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__content::before {\n border-radius: 50%;\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-700);\n}\n\n.lh-fraction__background {\n position: absolute;\n height: 100%;\n width: 100%;\n border-radius: calc(var(--gauge-circle-size) / 2);\n opacity: 0.1;\n z-index: -1;\n}\n\n.lh-fraction__content-wrapper {\n height: var(--gauge-circle-size);\n display: flex;\n align-items: center;\n}\n\n.lh-fraction__content {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: center;\n font-size: calc(0.3 * var(--gauge-circle-size));\n line-height: calc(0.4 * var(--gauge-circle-size));\n width: max-content;\n min-width: calc(1.5 * var(--gauge-circle-size));\n padding: calc(0.1 * var(--gauge-circle-size)) calc(0.2 * var(--gauge-circle-size));\n --score-icon-size: calc(0.21 * var(--gauge-circle-size));\n --score-icon-margin: 0 calc(0.15 * var(--gauge-circle-size)) 0 0;\n}\n\n.lh-gauge {\n stroke-linecap: round;\n width: var(--gauge-circle-size);\n height: var(--gauge-circle-size);\n}\n\n.lh-category .lh-gauge {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n.lh-gauge-base {\n opacity: 0.1;\n}\n\n.lh-gauge-arc {\n fill: none;\n transform-origin: 50% 50%;\n animation: load-gauge var(--transition-length) ease both;\n animation-delay: 250ms;\n}\n\n.lh-gauge__svg-wrapper {\n position: relative;\n height: var(--gauge-circle-size);\n}\n.lh-category .lh-gauge__svg-wrapper,\n.lh-category .lh-fraction__wrapper {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n/* The plugin badge overlay */\n.lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size);\n height: var(--plugin-badge-size);\n background-color: var(--plugin-badge-background-color);\n background-image: var(--plugin-icon-url);\n background-repeat: no-repeat;\n background-size: var(--plugin-icon-size);\n background-position: 58% 50%;\n content: \"\";\n position: absolute;\n right: -6px;\n bottom: 0px;\n display: block;\n z-index: 100;\n box-shadow: 0 0 4px rgba(0,0,0,.2);\n border-radius: 25%;\n}\n.lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size-big);\n height: var(--plugin-badge-size-big);\n}\n\n@keyframes load-gauge {\n from { stroke-dasharray: 0 352; }\n}\n\n.lh-gauge__percentage {\n width: 100%;\n height: var(--gauge-circle-size);\n position: absolute;\n font-family: var(--report-font-family-monospace);\n font-size: calc(var(--gauge-circle-size) * 0.34 + 1.3px);\n line-height: 0;\n text-align: center;\n top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 2);\n}\n\n.lh-category .lh-gauge__percentage {\n --gauge-circle-size: var(--gauge-circle-size-big);\n --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);\n}\n\n.lh-gauge__wrapper,\n.lh-fraction__wrapper {\n position: relative;\n display: flex;\n align-items: center;\n flex-direction: column;\n text-decoration: none;\n padding: var(--score-container-padding);\n\n --transition-length: 1s;\n\n /* Contain the layout style paint & layers during animation*/\n contain: content;\n will-change: opacity; /* Only using for layer promotion */\n}\n\n.lh-gauge__label,\n.lh-fraction__label {\n font-size: var(--gauge-label-font-size);\n font-weight: 500;\n line-height: var(--gauge-label-line-height);\n margin-top: 10px;\n text-align: center;\n color: var(--report-text-color);\n word-break: keep-all;\n}\n\n/* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */\n.lh-category .lh-gauge__label,\n.lh-category .lh-fraction__label {\n --gauge-label-font-size: var(--gauge-label-font-size-big);\n --gauge-label-line-height: var(--gauge-label-line-height-big);\n margin-top: 14px;\n}\n\n.lh-scores-header .lh-gauge__wrapper,\n.lh-scores-header .lh-fraction__wrapper,\n.lh-scores-header .lh-gauge--pwa__wrapper,\n.lh-sticky-header .lh-gauge__wrapper,\n.lh-sticky-header .lh-fraction__wrapper,\n.lh-sticky-header .lh-gauge--pwa__wrapper {\n width: var(--gauge-wrapper-width);\n}\n\n.lh-scorescale {\n display: inline-flex;\n\n gap: calc(var(--default-padding) * 4);\n margin: 16px auto 0 auto;\n font-size: var(--report-font-size-secondary);\n color: var(--color-gray-700);\n\n}\n\n.lh-scorescale-range {\n display: flex;\n align-items: center;\n font-family: var(--report-font-family-monospace);\n white-space: nowrap;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale {\n border: 0;\n display: flex;\n justify-content: center;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale-range {\n font-family: unset;\n font-size: 12px;\n}\n\n.lh-scorescale-wrap {\n display: contents;\n}\n\n/* Hide category score gauages if it's a single category report */\n.lh-header--solo-category .lh-scores-wrapper {\n display: none;\n}\n\n\n.lh-categories {\n width: 100%;\n}\n\n.lh-category {\n padding: var(--category-padding);\n max-width: var(--report-content-max-width);\n margin: 0 auto;\n\n --sticky-header-height: calc(var(--gauge-circle-size-sm) + var(--score-container-padding) * 2);\n --topbar-plus-sticky-header: calc(var(--topbar-height) + var(--sticky-header-height));\n scroll-margin-top: var(--topbar-plus-sticky-header);\n\n /* Faster recalc style & layout of the report. https://web.dev/content-visibility/ */\n content-visibility: auto;\n contain-intrinsic-size: 1000px;\n}\n\n.lh-category-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n.lh-category-wrapper:last-of-type {\n border-bottom: 0;\n}\n\n.lh-category-header {\n margin-bottom: var(--section-padding-vertical);\n}\n\n.lh-category-header .lh-score__gauge {\n max-width: 400px;\n width: auto;\n margin: 0px auto;\n}\n\n.lh-category-header__finalscreenshot {\n display: grid;\n grid-template: none / 1fr 1px 1fr;\n justify-items: center;\n align-items: center;\n gap: var(--report-line-height);\n min-height: 288px;\n margin-bottom: var(--default-padding);\n}\n\n.lh-final-ss-image {\n /* constrain the size of the image to not be too large */\n max-height: calc(var(--gauge-circle-size-big) * 2.8);\n max-width: calc(var(--gauge-circle-size-big) * 3.5);\n border: 1px solid var(--color-gray-200);\n padding: 4px;\n border-radius: 3px;\n display: block;\n}\n\n.lh-category-headercol--separator {\n background: var(--color-gray-200);\n width: 1px;\n height: var(--gauge-circle-size-big);\n}\n\n@media screen and (max-width: 780px) {\n .lh-category-header__finalscreenshot {\n grid-template: 1fr 1fr / none\n }\n .lh-category-headercol--separator {\n display: none;\n }\n}\n\n\n/* 964 fits the min-width of the filmstrip */\n@media screen and (max-width: 964px) {\n .lh-report {\n margin-left: 0;\n width: 100%;\n }\n}\n\n@media print {\n body {\n -webkit-print-color-adjust: exact; /* print background colors */\n }\n .lh-container {\n display: block;\n }\n .lh-report {\n margin-left: 0;\n padding-top: 0;\n }\n .lh-categories {\n margin-top: 0;\n }\n}\n\n.lh-table {\n position: relative;\n border-collapse: separate;\n border-spacing: 0;\n /* Can't assign padding to table, so shorten the width instead. */\n width: calc(100% - var(--audit-description-padding-left) - var(--stackpack-padding-horizontal));\n border: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-table thead th {\n position: sticky;\n top: calc(var(--topbar-plus-sticky-header) + 1em);\n z-index: 1;\n background-color: var(--report-background-color);\n border-bottom: 1px solid var(--report-border-color-secondary);\n font-weight: normal;\n color: var(--color-gray-600);\n /* See text-wrapping comment on .lh-container. */\n word-break: normal;\n}\n\n.lh-row--even {\n background-color: var(--table-higlight-background-color);\n}\n.lh-row--hidden {\n display: none;\n}\n\n.lh-table th,\n.lh-table td {\n padding: var(--default-padding);\n}\n\n.lh-table tr {\n vertical-align: middle;\n}\n\n/* Looks unnecessary, but mostly for keeping the s left-aligned */\n.lh-table-column--text,\n.lh-table-column--source-location,\n.lh-table-column--url,\n/* .lh-table-column--thumbnail, */\n/* .lh-table-column--empty,*/\n.lh-table-column--code,\n.lh-table-column--node {\n text-align: left;\n}\n\n.lh-table-column--code {\n min-width: 100px;\n}\n\n.lh-table-column--bytes,\n.lh-table-column--timespanMs,\n.lh-table-column--ms,\n.lh-table-column--numeric {\n text-align: right;\n word-break: normal;\n}\n\n\n\n.lh-table .lh-table-column--thumbnail {\n width: var(--image-preview-size);\n}\n\n.lh-table-column--url {\n min-width: 250px;\n}\n\n.lh-table-column--text {\n min-width: 80px;\n}\n\n/* Keep columns narrow if they follow the URL column */\n/* 12% was determined to be a decent narrow width, but wide enough for column headings */\n.lh-table-column--url + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--ms,\n.lh-table-column--url + .lh-table-column--ms + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--timespanMs {\n width: 12%;\n}\n\n.lh-text__url-host {\n display: inline;\n}\n\n.lh-text__url-host {\n margin-left: calc(var(--report-font-size) / 2);\n opacity: 0.6;\n font-size: 90%\n}\n\n.lh-thumbnail {\n object-fit: cover;\n width: var(--image-preview-size);\n height: var(--image-preview-size);\n display: block;\n}\n\n.lh-unknown pre {\n overflow: scroll;\n border: solid 1px var(--color-gray-200);\n}\n\n.lh-text__url > a {\n color: inherit;\n text-decoration: none;\n}\n\n.lh-text__url > a:hover {\n text-decoration: underline dotted #999;\n}\n\n.lh-sub-item-row {\n margin-left: 20px;\n margin-bottom: 0;\n color: var(--color-gray-700);\n}\n.lh-sub-item-row td {\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 20px;\n}\n\n/* Chevron\n https://codepen.io/paulirish/pen/LmzEmK\n */\n.lh-chevron {\n --chevron-angle: 42deg;\n /* Edge doesn't support transform: rotate(calc(...)), so we define it here */\n --chevron-angle-right: -42deg;\n width: var(--chevron-size);\n height: var(--chevron-size);\n margin-top: calc((var(--report-line-height) - 12px) / 2);\n}\n\n.lh-chevron__lines {\n transition: transform 0.4s;\n transform: translateY(var(--report-line-height));\n}\n.lh-chevron__line {\n stroke: var(--chevron-line-stroke);\n stroke-width: var(--chevron-size);\n stroke-linecap: square;\n transform-origin: 50%;\n transform: rotate(var(--chevron-angle));\n transition: transform 300ms, stroke 300ms;\n}\n\n.lh-expandable-details .lh-chevron__line-right,\n.lh-expandable-details[open] .lh-chevron__line-left {\n transform: rotate(var(--chevron-angle-right));\n}\n\n.lh-expandable-details[open] .lh-chevron__line-right {\n transform: rotate(var(--chevron-angle));\n}\n\n\n.lh-expandable-details[open] .lh-chevron__lines {\n transform: translateY(calc(var(--chevron-size) * -1));\n}\n\n.lh-expandable-details[open] {\n animation: 300ms openDetails forwards;\n padding-bottom: var(--default-padding);\n}\n\n@keyframes openDetails {\n from {\n outline: 1px solid var(--report-background-color);\n }\n to {\n outline: 1px solid;\n box-shadow: 0 2px 4px rgba(0, 0, 0, .24);\n }\n}\n\n@media screen and (max-width: 780px) {\n /* no black outline if we're not confident the entire table can be displayed within bounds */\n .lh-expandable-details[open] {\n animation: none;\n }\n}\n\n.lh-expandable-details[open] summary, details.lh-clump > summary {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\ndetails.lh-clump[open] > summary {\n border-bottom-width: 0;\n}\n\n\n\ndetails .lh-clump-toggletext--hide,\ndetails[open] .lh-clump-toggletext--show { display: none; }\ndetails[open] .lh-clump-toggletext--hide { display: block;}\n\n\n/* Tooltip */\n.lh-tooltip-boundary {\n position: relative;\n}\n\n.lh-tooltip {\n position: absolute;\n display: none; /* Don't retain these layers when not needed */\n opacity: 0;\n background: #ffffff;\n white-space: pre-line; /* Render newlines in the text */\n min-width: 246px;\n max-width: 275px;\n padding: 15px;\n border-radius: 5px;\n text-align: initial;\n line-height: 1.4;\n}\n/* shrink tooltips to not be cutoff on left edge of narrow viewports\n 45vw is chosen to be ~= width of the left column of metrics\n*/\n@media screen and (max-width: 535px) {\n .lh-tooltip {\n min-width: 45vw;\n padding: 3vw;\n }\n}\n\n.lh-tooltip-boundary:hover .lh-tooltip {\n display: block;\n animation: fadeInTooltip 250ms;\n animation-fill-mode: forwards;\n animation-delay: 850ms;\n bottom: 100%;\n z-index: 1;\n will-change: opacity;\n right: 0;\n pointer-events: none;\n}\n\n.lh-tooltip::before {\n content: \"\";\n border: solid transparent;\n border-bottom-color: #fff;\n border-width: 10px;\n position: absolute;\n bottom: -20px;\n right: 6px;\n transform: rotate(180deg);\n pointer-events: none;\n}\n\n@keyframes fadeInTooltip {\n 0% { opacity: 0; }\n 75% { opacity: 1; }\n 100% { opacity: 1; filter: drop-shadow(1px 0px 1px #aaa) drop-shadow(0px 2px 4px hsla(206, 6%, 25%, 0.15)); pointer-events: auto; }\n}\n\n/* Element screenshot */\n.lh-element-screenshot {\n float: left;\n margin-right: 20px;\n}\n.lh-element-screenshot__content {\n overflow: hidden;\n min-width: 110px;\n display: flex;\n justify-content: center;\n background-color: var(--report-background-color);\n}\n.lh-element-screenshot__image {\n position: relative;\n /* Set by ElementScreenshotRenderer.installFullPageScreenshotCssVariable */\n background-image: var(--element-screenshot-url);\n outline: 2px solid #777;\n background-color: white;\n background-repeat: no-repeat;\n}\n.lh-element-screenshot__mask {\n position: absolute;\n background: #555;\n opacity: 0.8;\n}\n.lh-element-screenshot__element-marker {\n position: absolute;\n outline: 2px solid var(--color-lime-400);\n}\n.lh-element-screenshot__overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2000; /* .lh-topbar is 1000 */\n background: var(--screenshot-overlay-background);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n\n.lh-element-screenshot__overlay .lh-element-screenshot {\n margin-right: 0; /* clearing margin used in thumbnail case */\n outline: 1px solid var(--color-gray-700);\n}\n\n.lh-screenshot-overlay--enabled .lh-element-screenshot {\n cursor: zoom-out;\n}\n.lh-screenshot-overlay--enabled .lh-node .lh-element-screenshot {\n cursor: zoom-in;\n}\n\n\n.lh-meta__items {\n --meta-icon-size: calc(var(--report-icon-size) * 0.667);\n padding: var(--default-padding);\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n background-color: var(--env-item-background-color);\n border-radius: 3px;\n margin: 0 0 var(--default-padding) 0;\n font-size: 12px;\n column-gap: var(--default-padding);\n color: var(--color-gray-700);\n}\n\n.lh-meta__item {\n display: block;\n list-style-type: none;\n position: relative;\n padding: 0 0 0 calc(var(--meta-icon-size) + var(--default-padding) * 2);\n cursor: unset; /* disable pointer cursor from report-icon */\n}\n\n.lh-meta__item.lh-tooltip-boundary {\n text-decoration: dotted underline var(--color-gray-500);\n cursor: help;\n}\n\n.lh-meta__item.lh-report-icon::before {\n position: absolute;\n left: var(--default-padding);\n width: var(--meta-icon-size);\n height: var(--meta-icon-size);\n}\n\n.lh-meta__item.lh-report-icon:hover::before {\n opacity: 0.7;\n}\n\n.lh-meta__item .lh-tooltip {\n color: var(--color-gray-800);\n}\n\n.lh-meta__item .lh-tooltip::before {\n right: auto; /* Set the tooltip arrow to the leftside */\n left: 6px;\n}\n\n/* Change the grid for narrow viewport. */\n@media screen and (max-width: 640px) {\n .lh-meta__items {\n grid-template-columns: 1fr 1fr;\n }\n}\n@media screen and (max-width: 535px) {\n .lh-meta__items {\n display: block;\n }\n}\n\n\n/*# sourceURL=report-styles.css */\n"); + el1.append("/**\n * @license\n * Copyright 2017 The Lighthouse Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS-IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Naming convention:\n\n If a variable is used for a specific component: --{component}-{property name}-{modifier}\n\n Both {component} and {property name} should be kebab-case. If the target is the entire page,\n use 'report' for the component. The property name should not be abbreviated. Use the\n property name the variable is intended for - if it's used for multiple, a common descriptor\n is fine (ex: 'size' for a variable applied to 'width' and 'height'). If a variable is shared\n across multiple components, either create more variables or just drop the \"{component}-\"\n part of the name. Append any modifiers at the end (ex: 'big', 'dark').\n\n For colors: --color-{hue}-{intensity}\n\n {intensity} is the Material Design tag - 700, A700, etc.\n*/\n.lh-vars {\n /* Palette using Material Design Colors\n * https://www.materialui.co/colors */\n --color-amber-50: #FFF8E1;\n --color-blue-200: #90CAF9;\n --color-blue-900: #0D47A1;\n --color-blue-A700: #2962FF;\n --color-blue-primary: #06f;\n --color-cyan-500: #00BCD4;\n --color-gray-100: #F5F5F5;\n --color-gray-300: #CFCFCF;\n --color-gray-200: #E0E0E0;\n --color-gray-400: #BDBDBD;\n --color-gray-50: #FAFAFA;\n --color-gray-500: #9E9E9E;\n --color-gray-600: #757575;\n --color-gray-700: #616161;\n --color-gray-800: #424242;\n --color-gray-900: #212121;\n --color-gray: #000000;\n --color-green-700: #080;\n --color-green: #0c6;\n --color-lime-400: #D3E156;\n --color-orange-50: #FFF3E0;\n --color-orange-700: #C33300;\n --color-orange: #fa3;\n --color-red-700: #c00;\n --color-red: #f33;\n --color-teal-600: #00897B;\n --color-white: #FFFFFF;\n\n /* Context-specific colors */\n --color-average-secondary: var(--color-orange-700);\n --color-average: var(--color-orange);\n --color-fail-secondary: var(--color-red-700);\n --color-fail: var(--color-red);\n --color-hover: var(--color-gray-50);\n --color-informative: var(--color-blue-900);\n --color-pass-secondary: var(--color-green-700);\n --color-pass: var(--color-green);\n --color-not-applicable: var(--color-gray-600);\n\n /* Component variables */\n --audit-description-padding-left: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right));\n --audit-explanation-line-height: 16px;\n --audit-group-margin-bottom: calc(var(--default-padding) * 6);\n --audit-group-padding-vertical: 8px;\n --audit-margin-horizontal: 5px;\n --audit-padding-vertical: 8px;\n --category-padding: calc(var(--default-padding) * 6) var(--edge-gap-padding) calc(var(--default-padding) * 4);\n --chevron-line-stroke: var(--color-gray-600);\n --chevron-size: 12px;\n --default-padding: 8px;\n --edge-gap-padding: calc(var(--default-padding) * 4);\n --env-item-background-color: var(--color-gray-100);\n --env-item-font-size: 28px;\n --env-item-line-height: 36px;\n --env-item-padding: 10px 0px;\n --env-name-min-width: 220px;\n --footer-padding-vertical: 16px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 48px;\n --gauge-circle-size-sm: 32px;\n --gauge-label-font-size-big: 18px;\n --gauge-label-font-size: var(--report-font-size-secondary);\n --gauge-label-line-height-big: 24px;\n --gauge-label-line-height: var(--report-line-height-secondary);\n --gauge-percentage-font-size-big: 38px;\n --gauge-percentage-font-size: var(--report-font-size-secondary);\n --gauge-wrapper-width: 120px;\n --header-line-height: 24px;\n --highlighter-background-color: var(--report-text-color);\n --icon-square-size: calc(var(--score-icon-size) * 0.88);\n --image-preview-size: 48px;\n --link-color: var(--color-blue-primary);\n --locale-selector-background-color: var(--color-white);\n --metric-toggle-lines-fill: #7F7F7F;\n --metric-value-font-size: calc(var(--report-font-size) * 1.8);\n --metrics-toggle-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-white);\n --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);\n --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);\n --plugin-icon-size: 65%;\n --pwa-icon-margin: 0 var(--default-padding);\n --pwa-icon-size: var(--topbar-logo-size);\n --report-background-color: #fff;\n --report-border-color-secondary: #ebebeb;\n --report-font-family-monospace: 'Roboto Mono', 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: Roboto, Helvetica, Arial, sans-serif;\n --report-font-size: 14px;\n --report-font-size-secondary: 12px;\n --report-icon-size: var(--score-icon-background-size);\n --report-line-height: 24px;\n --report-line-height-secondary: 20px;\n --report-monospace-font-size: calc(var(--report-font-size) * 0.85);\n --report-text-color-secondary: var(--color-gray-800);\n --report-text-color: var(--color-gray-900);\n --report-content-max-width: calc(60 * var(--report-font-size)); /* defaults to 840px */\n --report-content-min-width: 360px;\n --report-content-max-width-minus-edge-gap: calc(var(--report-content-max-width) - var(--edge-gap-padding) * 2);\n --score-container-padding: 8px;\n --score-icon-background-size: 24px;\n --score-icon-margin-left: 6px;\n --score-icon-margin-right: 14px;\n --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);\n --score-icon-size: 12px;\n --score-icon-size-big: 16px;\n --screenshot-overlay-background: rgba(0, 0, 0, 0.3);\n --section-padding-vertical: calc(var(--default-padding) * 6);\n --snippet-background-color: var(--color-gray-50);\n --snippet-color: #0938C2;\n --sparkline-height: 5px;\n --stackpack-padding-horizontal: 10px;\n --sticky-header-background-color: var(--report-background-color);\n --table-higlight-background-color: hsla(210, 17%, 77%, 0.1);\n --tools-icon-color: var(--color-gray-600);\n --topbar-background-color: var(--color-white);\n --topbar-height: 32px;\n --topbar-logo-size: 24px;\n --topbar-padding: 0 8px;\n --toplevel-warning-background-color: hsla(30, 100%, 75%, 10%);\n --toplevel-warning-message-text-color: var(--color-average-secondary);\n --toplevel-warning-padding: 18px;\n --toplevel-warning-text-color: var(--report-text-color);\n\n /* SVGs */\n --plugin-icon-url-dark: url('data:image/svg+xml;utf8,');\n --plugin-icon-url: url('data:image/svg+xml;utf8,');\n\n --pass-icon-url: url('data:image/svg+xml;utf8,check');\n --average-icon-url: url('data:image/svg+xml;utf8,info');\n --fail-icon-url: url('data:image/svg+xml;utf8,warn');\n\n --pwa-installable-gray-url: url('data:image/svg+xml;utf8,');\n --pwa-optimized-gray-url: url('data:image/svg+xml;utf8,');\n\n --pwa-installable-gray-url-dark: url('data:image/svg+xml;utf8,');\n --pwa-optimized-gray-url-dark: url('data:image/svg+xml;utf8,');\n\n --pwa-installable-color-url: url('data:image/svg+xml;utf8,');\n --pwa-optimized-color-url: url('data:image/svg+xml;utf8,');\n\n --swap-locale-icon-url: url('data:image/svg+xml;utf8,');\n}\n\n@media not print {\n .lh-dark {\n /* Pallete */\n --color-gray-200: var(--color-gray-800);\n --color-gray-300: #616161;\n --color-gray-400: var(--color-gray-600);\n --color-gray-700: var(--color-gray-400);\n --color-gray-50: #757575;\n --color-gray-600: var(--color-gray-500);\n --color-green-700: var(--color-green);\n --color-orange-700: var(--color-orange);\n --color-red-700: var(--color-red);\n --color-teal-600: var(--color-cyan-500);\n\n /* Context-specific colors */\n --color-hover: rgba(0, 0, 0, 0.2);\n --color-informative: var(--color-blue-200);\n\n /* Component variables */\n --env-item-background-color: #393535;\n --link-color: var(--color-blue-200);\n --locale-selector-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-gray-800);\n --report-background-color: var(--color-gray-900);\n --report-border-color-secondary: var(--color-gray-200);\n --report-text-color-secondary: var(--color-gray-400);\n --report-text-color: var(--color-gray-100);\n --snippet-color: var(--color-cyan-500);\n --topbar-background-color: var(--color-gray);\n --toplevel-warning-background-color: hsl(33deg 14% 18%);\n --toplevel-warning-message-text-color: var(--color-orange-700);\n --toplevel-warning-text-color: var(--color-gray-100);\n\n /* SVGs */\n --plugin-icon-url: var(--plugin-icon-url-dark);\n --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark);\n --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark);\n }\n}\n\n@media only screen and (max-width: 480px) {\n .lh-vars {\n --audit-group-margin-bottom: 20px;\n --edge-gap-padding: var(--default-padding);\n --env-name-min-width: 120px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 72px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 112px;\n --header-padding: 16px 0 16px 0;\n --image-preview-size: 24px;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-size: 14px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --topbar-height: 28px;\n --topbar-logo-size: 20px;\n }\n\n /* Not enough space to adequately show the relative savings bars. */\n .lh-sparkline {\n display: none;\n }\n}\n\n.lh-vars.lh-devtools {\n --audit-explanation-line-height: 14px;\n --audit-group-margin-bottom: 20px;\n --audit-group-padding-vertical: 12px;\n --audit-padding-vertical: 4px;\n --category-padding: 12px;\n --default-padding: 12px;\n --env-name-min-width: 120px;\n --footer-padding-vertical: 8px;\n --gauge-circle-size-big: 72px;\n --gauge-circle-size: 64px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 97px;\n --header-line-height: 20px;\n --header-padding: 16px 0 16px 0;\n --screenshot-overlay-background: transparent;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-family-monospace: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif;\n --report-font-size: 12px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --section-padding-vertical: 8px;\n}\n\n.lh-devtools.lh-root {\n height: 100%;\n}\n.lh-devtools.lh-root img {\n /* Override devtools default 'min-width: 0' so svg without size in a flexbox isn't collapsed. */\n min-width: auto;\n}\n.lh-devtools .lh-container {\n overflow-y: scroll;\n height: calc(100% - var(--topbar-height));\n}\n@media print {\n .lh-devtools .lh-container {\n overflow: unset;\n }\n}\n.lh-devtools .lh-sticky-header {\n /* This is normally the height of the topbar, but we want it to stick to the top of our scroll container .lh-container` */\n top: 0;\n}\n.lh-devtools .lh-element-screenshot__overlay {\n position: absolute;\n}\n\n@keyframes fadeIn {\n 0% { opacity: 0;}\n 100% { opacity: 0.6;}\n}\n\n.lh-root *, .lh-root *::before, .lh-root *::after {\n box-sizing: border-box;\n}\n\n.lh-root {\n font-family: var(--report-font-family);\n font-size: var(--report-font-size);\n margin: 0;\n line-height: var(--report-line-height);\n background: var(--report-background-color);\n color: var(--report-text-color);\n}\n\n.lh-root :focus-visible {\n outline: -webkit-focus-ring-color auto 3px;\n}\n.lh-root summary:focus {\n outline: none;\n box-shadow: 0 0 0 1px hsl(217, 89%, 61%);\n}\n\n.lh-root [hidden] {\n display: none !important;\n}\n\n.lh-root pre {\n margin: 0;\n}\n\n.lh-root details > summary {\n cursor: pointer;\n}\n\n.lh-hidden {\n display: none !important;\n}\n\n.lh-container {\n /*\n Text wrapping in the report is so much FUN!\n We have a `word-break: break-word;` globally here to prevent a few common scenarios, namely\n long non-breakable text (usually URLs) found in:\n 1. The footer\n 2. .lh-node (outerHTML)\n 3. .lh-code\n\n With that sorted, the next challenge is appropriate column sizing and text wrapping inside our\n .lh-details tables. Even more fun.\n * We don't want table headers (\"Potential Savings (ms)\") to wrap or their column values, but\n we'd be happy for the URL column to wrap if the URLs are particularly long.\n * We want the narrow columns to remain narrow, providing the most column width for URL\n * We don't want the table to extend past 100% width.\n * Long URLs in the URL column can wrap. Util.getURLDisplayName maxes them out at 64 characters,\n but they do not get any overflow:ellipsis treatment.\n */\n word-break: break-word;\n}\n\n.lh-audit-group a,\n.lh-category-header__description a,\n.lh-audit__description a,\n.lh-warnings a,\n.lh-footer a,\n.lh-table-column--link a {\n color: var(--link-color);\n}\n\n.lh-audit__description, .lh-audit__stackpack {\n --inner-audit-padding-right: var(--stackpack-padding-horizontal);\n padding-left: var(--audit-description-padding-left);\n padding-right: var(--inner-audit-padding-right);\n padding-top: 8px;\n padding-bottom: 8px;\n}\n\n.lh-details {\n margin-top: var(--default-padding);\n margin-bottom: var(--default-padding);\n margin-left: var(--audit-description-padding-left);\n /* whatever the .lh-details side margins are */\n width: 100%;\n}\n\n.lh-audit__stackpack {\n display: flex;\n align-items: center;\n}\n\n.lh-audit__stackpack__img {\n max-width: 30px;\n margin-right: var(--default-padding)\n}\n\n/* Report header */\n\n.lh-report-icon {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n cursor: pointer;\n}\n.lh-report-icon[disabled] {\n opacity: 0.3;\n pointer-events: none;\n}\n\n.lh-report-icon::before {\n content: \"\";\n margin: 4px;\n background-repeat: no-repeat;\n width: var(--report-icon-size);\n height: var(--report-icon-size);\n opacity: 0.7;\n display: inline-block;\n vertical-align: middle;\n}\n.lh-report-icon:hover::before {\n opacity: 1;\n}\n.lh-dark .lh-report-icon::before {\n filter: invert(1);\n}\n.lh-report-icon--print::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--copy::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--open::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--download::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--dark::before {\n background-image:url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--treemap::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--date::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--devices::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--world::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--stopwatch::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--networkspeed::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--samples-one::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--samples-many::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n.lh-report-icon--chrome::before {\n background-image: url('data:image/svg+xml;utf8,');\n}\n\n\n\n.lh-buttons {\n display: flex;\n flex-wrap: wrap;\n margin: var(--default-padding) 0;\n}\n.lh-button {\n height: 32px;\n border: 1px solid var(--report-border-color-secondary);\n border-radius: 3px;\n color: var(--link-color);\n background-color: var(--report-background-color);\n margin: 5px;\n}\n\n.lh-button:first-of-type {\n margin-left: 0;\n}\n\n/* Node */\n.lh-node__snippet {\n font-family: var(--report-font-family-monospace);\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n line-height: 20px;\n}\n\n/* Score */\n\n.lh-audit__score-icon {\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n margin: var(--score-icon-margin);\n}\n\n.lh-audit--pass .lh-audit__display-text {\n color: var(--color-pass-secondary);\n}\n.lh-audit--pass .lh-audit__score-icon,\n.lh-scorescale-range--pass::before {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-audit__display-text {\n color: var(--color-average-secondary);\n}\n.lh-audit--average .lh-audit__score-icon,\n.lh-scorescale-range--average::before {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-audit--fail .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n.lh-audit--fail .lh-audit__score-icon,\n.lh-audit--error .lh-audit__score-icon,\n.lh-scorescale-range--fail::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-audit--manual .lh-audit__display-text,\n.lh-audit--notapplicable .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n.lh-audit--manual .lh-audit__score-icon,\n.lh-audit--notapplicable .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n background: none;\n}\n\n.lh-audit--informative .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n\n.lh-audit--informative .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n}\n\n.lh-audit__description,\n.lh-audit__stackpack {\n color: var(--report-text-color-secondary);\n}\n.lh-audit__adorn {\n border: 1px solid slategray;\n border-radius: 3px;\n margin: 0 3px;\n padding: 0 2px;\n line-height: 1.1;\n display: inline-block;\n font-size: 90%;\n}\n\n.lh-category-header__description {\n text-align: center;\n color: var(--color-gray-700);\n margin: 0px auto;\n max-width: 400px;\n}\n\n\n.lh-audit__display-text,\n.lh-load-opportunity__sparkline,\n.lh-chevron-container {\n margin: 0 var(--audit-margin-horizontal);\n}\n.lh-chevron-container {\n margin-right: 0;\n}\n\n.lh-audit__title-and-text {\n flex: 1;\n}\n\n.lh-audit__title-and-text code {\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n}\n\n/* Prepend display text with em dash separator. But not in Opportunities. */\n.lh-audit__display-text:not(:empty):before {\n content: '—';\n margin-right: var(--audit-margin-horizontal);\n}\n.lh-audit-group.lh-audit-group--load-opportunities .lh-audit__display-text:not(:empty):before {\n display: none;\n}\n\n/* Expandable Details (Audit Groups, Audits) */\n.lh-audit__header {\n display: flex;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-audit--load-opportunity .lh-audit__header {\n display: block;\n}\n\n\n.lh-metricfilter {\n display: grid;\n justify-content: end;\n align-items: center;\n grid-auto-flow: column;\n gap: 4px;\n color: var(--color-gray-700);\n}\n\n.lh-metricfilter__radio {\n position: absolute;\n left: -9999px;\n}\n.lh-metricfilter input[type='radio']:focus-visible + label {\n outline: -webkit-focus-ring-color auto 1px;\n}\n\n.lh-metricfilter__label {\n display: inline-flex;\n padding: 0 4px;\n height: 16px;\n text-decoration: underline;\n align-items: center;\n cursor: pointer;\n font-size: 90%;\n}\n\n.lh-metricfilter__label--active {\n background: var(--color-blue-primary);\n color: var(--color-white);\n border-radius: 3px;\n text-decoration: none;\n}\n/* Give the 'All' choice a more muted display */\n.lh-metricfilter__label--active[for=\"metric-All\"] {\n background-color: var(--color-blue-200) !important;\n color: black !important;\n}\n\n.lh-metricfilter__text {\n margin-right: 8px;\n}\n\n/* If audits are filtered, hide the itemcount for Passed Audits… */\n.lh-category--filtered .lh-audit-group .lh-audit-group__itemcount {\n display: none;\n}\n\n\n.lh-audit__header:hover {\n background-color: var(--color-hover);\n}\n\n/* We want to hide the browser's default arrow marker on summary elements. Admittedly, it's complicated. */\n.lh-root details > summary {\n /* Blink 89+ and Firefox will hide the arrow when display is changed from (new) default of `list-item` to block. https://chromestatus.com/feature/6730096436051968*/\n display: block;\n}\n/* Safari and Blink <=88 require using the -webkit-details-marker selector */\n.lh-root details > summary::-webkit-details-marker {\n display: none;\n}\n\n/* Perf Metric */\n\n.lh-metrics-container {\n display: grid;\n grid-auto-rows: 1fr;\n grid-template-columns: 1fr 1fr;\n grid-column-gap: var(--report-line-height);\n margin-bottom: var(--default-padding);\n}\n\n.lh-metric {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(-n+2) {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-metric__innerwrap {\n display: grid;\n /**\n * Icon -- Metric Name\n * -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 1fr;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-metric__details {\n order: -1;\n}\n\n.lh-metric__title {\n flex: 1;\n}\n\n.lh-calclink {\n padding-left: calc(1ex / 3);\n}\n\n.lh-metric__description {\n display: none;\n grid-column-start: 2;\n grid-column-end: 4;\n color: var(--report-text-color-secondary);\n}\n\n.lh-metric__value {\n font-size: var(--metric-value-font-size);\n margin: calc(var(--default-padding) / 2) 0;\n white-space: nowrap; /* No wrapping between metric value and the icon */\n grid-column-start: 2;\n}\n\n\n@media screen and (max-width: 535px) {\n .lh-metrics-container {\n display: block;\n }\n\n .lh-metric {\n border-bottom: none !important;\n }\n .lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(1) {\n border-bottom: 1px solid var(--report-border-color-secondary) !important;\n }\n\n /* Change the grid to 3 columns for narrow viewport. */\n .lh-metric__innerwrap {\n /**\n * Icon -- Metric Name -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 2fr 1fr;\n }\n .lh-metric__value {\n justify-self: end;\n grid-column-start: unset;\n }\n}\n\n/* No-JS toggle switch */\n/* Keep this selector sync'd w/ `magicSelector` in report-ui-features-test.js */\n .lh-metrics-toggle__input:checked ~ .lh-metrics-container .lh-metric__description {\n display: block;\n}\n\n/* TODO get rid of the SVGS and clean up these some more */\n.lh-metrics-toggle__input {\n opacity: 0;\n position: absolute;\n right: 0;\n top: 0px;\n}\n\n.lh-metrics-toggle__input + div > label > .lh-metrics-toggle__labeltext--hide,\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--show {\n display: none;\n}\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--hide {\n display: inline;\n}\n.lh-metrics-toggle__input:focus + div > label {\n outline: -webkit-focus-ring-color auto 3px;\n}\n\n.lh-metrics-toggle__label {\n cursor: pointer;\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n/* Pushes the metric description toggle button to the right. */\n.lh-audit-group--metrics .lh-audit-group__header {\n display: flex;\n justify-content: space-between;\n}\n\n.lh-metric__icon,\n.lh-scorescale-range::before {\n content: '';\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n display: inline-block;\n margin: var(--score-icon-margin);\n}\n\n.lh-metric--pass .lh-metric__value {\n color: var(--color-pass-secondary);\n}\n.lh-metric--pass .lh-metric__icon {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-metric--average .lh-metric__value {\n color: var(--color-average-secondary);\n}\n.lh-metric--average .lh-metric__icon {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-metric--fail .lh-metric__value {\n color: var(--color-fail-secondary);\n}\n.lh-metric--fail .lh-metric__icon,\n.lh-metric--error .lh-metric__icon {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-metric--error .lh-metric__value,\n.lh-metric--error .lh-metric__description {\n color: var(--color-fail-secondary);\n}\n\n/* Perf load opportunity */\n\n.lh-load-opportunity__cols {\n display: flex;\n align-items: flex-start;\n}\n\n.lh-load-opportunity__header .lh-load-opportunity__col {\n color: var(--color-gray-600);\n display: unset;\n line-height: calc(2.3 * var(--report-font-size));\n}\n\n.lh-load-opportunity__col {\n display: flex;\n}\n\n.lh-load-opportunity__col--one {\n flex: 5;\n align-items: center;\n margin-right: 2px;\n}\n.lh-load-opportunity__col--two {\n flex: 4;\n text-align: right;\n}\n\n.lh-audit--load-opportunity .lh-audit__display-text {\n text-align: right;\n flex: 0 0 calc(4 * var(--report-font-size));\n}\n\n\n/* Sparkline */\n\n.lh-load-opportunity__sparkline {\n flex: 1;\n margin-top: calc((var(--report-line-height) - var(--sparkline-height)) / 2);\n}\n\n.lh-sparkline {\n height: var(--sparkline-height);\n width: 100%;\n}\n\n.lh-sparkline__bar {\n height: 100%;\n float: right;\n}\n\n.lh-audit--pass .lh-sparkline__bar {\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-sparkline__bar {\n background: var(--color-average);\n}\n\n.lh-audit--fail .lh-sparkline__bar {\n background: var(--color-fail);\n}\n\n/* Filmstrip */\n\n.lh-filmstrip-container {\n /* smaller gap between metrics and filmstrip */\n margin: -8px auto 0 auto;\n}\n\n.lh-filmstrip {\n display: grid;\n justify-content: space-between;\n padding-bottom: var(--default-padding);\n width: 100%;\n grid-template-columns: repeat(auto-fit, 60px);\n}\n\n.lh-filmstrip__frame {\n text-align: right;\n position: relative;\n}\n\n.lh-filmstrip__thumbnail {\n border: 1px solid var(--report-border-color-secondary);\n max-height: 100px;\n max-width: 60px;\n}\n\n/* Audit */\n\n.lh-audit {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n/* Apply border-top to just the first audit. */\n.lh-audit {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n.lh-audit ~ .lh-audit {\n border-top: none;\n}\n\n\n.lh-audit--error .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n\n/* Audit Group */\n\n.lh-audit-group {\n margin-bottom: var(--audit-group-margin-bottom);\n position: relative;\n}\n.lh-audit-group--metrics {\n margin-bottom: calc(var(--audit-group-margin-bottom) / 2);\n}\n\n.lh-audit-group__header::before {\n /* By default, groups don't get an icon */\n content: none;\n width: var(--pwa-icon-size);\n height: var(--pwa-icon-size);\n margin: var(--pwa-icon-margin);\n display: inline-block;\n vertical-align: middle;\n}\n\n/* Style the \"over budget\" columns red. */\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(4),\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(5),\n.lh-audit-group--budgets #timing-budget tbody tr td:nth-child(3) {\n color: var(--color-red-700);\n}\n\n/* Align the \"over budget request count\" text to be close to the \"over budget bytes\" column. */\n.lh-audit-group--budgets .lh-table tbody tr td:nth-child(4){\n text-align: right;\n}\n\n.lh-audit-group--budgets .lh-details--budget {\n width: 100%;\n margin: 0 0 var(--default-padding);\n}\n\n.lh-audit-group--pwa-installable .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-installable-gray-url);\n}\n.lh-audit-group--pwa-optimized .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-optimized-gray-url);\n}\n.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-installable-color-url);\n}\n.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-optimized-color-url);\n}\n\n.lh-audit-group--metrics .lh-audit-group__summary {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.lh-audit-group__summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.lh-audit-group__header .lh-chevron {\n margin-top: calc((var(--report-line-height) - 5px) / 2);\n}\n\n.lh-audit-group__header {\n letter-spacing: 0.8px;\n padding: var(--default-padding);\n padding-left: 0;\n}\n\n.lh-audit-group__header, .lh-audit-group__summary {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n.lh-audit-group__title {\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.lh-audit-group__itemcount {\n color: var(--color-gray-600);\n}\n\n.lh-audit-group__footer {\n color: var(--color-gray-600);\n display: block;\n margin-top: var(--default-padding);\n}\n\n.lh-details,\n.lh-category-header__description,\n.lh-load-opportunity__header,\n.lh-audit-group__footer {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n}\n\n.lh-audit-explanation {\n margin: var(--audit-padding-vertical) 0 calc(var(--audit-padding-vertical) / 2) var(--audit-margin-horizontal);\n line-height: var(--audit-explanation-line-height);\n display: inline-block;\n}\n\n.lh-audit--fail .lh-audit-explanation {\n color: var(--color-fail-secondary);\n}\n\n/* Report */\n.lh-list > :not(:last-child) {\n margin-bottom: calc(var(--default-padding) * 2);\n}\n\n.lh-header-container {\n display: block;\n margin: 0 auto;\n position: relative;\n word-wrap: break-word;\n}\n\n.lh-header-container .lh-scores-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n\n\n.lh-report {\n min-width: var(--report-content-min-width);\n}\n\n.lh-exception {\n font-size: large;\n}\n\n.lh-code {\n white-space: normal;\n margin-top: 0;\n font-size: var(--report-monospace-font-size);\n}\n\n.lh-warnings {\n --item-margin: calc(var(--report-line-height) / 6);\n color: var(--color-average-secondary);\n margin: var(--audit-padding-vertical) 0;\n padding: var(--default-padding)\n var(--default-padding)\n var(--default-padding)\n calc(var(--audit-description-padding-left));\n background-color: var(--toplevel-warning-background-color);\n}\n.lh-warnings span {\n font-weight: bold;\n}\n\n.lh-warnings--toplevel {\n --item-margin: calc(var(--header-line-height) / 4);\n color: var(--toplevel-warning-text-color);\n margin-left: auto;\n margin-right: auto;\n max-width: var(--report-content-max-width-minus-edge-gap);\n padding: var(--toplevel-warning-padding);\n border-radius: 8px;\n}\n\n.lh-warnings__msg {\n color: var(--toplevel-warning-message-text-color);\n margin: 0;\n}\n\n.lh-warnings ul {\n margin: 0;\n}\n.lh-warnings li {\n margin: var(--item-margin) 0;\n}\n.lh-warnings li:last-of-type {\n margin-bottom: 0;\n}\n\n.lh-scores-header {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n}\n.lh-scores-header__solo {\n padding: 0;\n border: 0;\n}\n\n/* Gauge */\n\n.lh-gauge__wrapper--pass {\n color: var(--color-pass-secondary);\n fill: var(--color-pass);\n stroke: var(--color-pass);\n}\n\n.lh-gauge__wrapper--average {\n color: var(--color-average-secondary);\n fill: var(--color-average);\n stroke: var(--color-average);\n}\n\n.lh-gauge__wrapper--fail {\n color: var(--color-fail-secondary);\n fill: var(--color-fail);\n stroke: var(--color-fail);\n}\n\n.lh-gauge__wrapper--not-applicable {\n color: var(--color-not-applicable);\n fill: var(--color-not-applicable);\n stroke: var(--color-not-applicable);\n}\n\n.lh-fraction__wrapper .lh-fraction__content::before {\n content: '';\n height: var(--score-icon-size);\n width: var(--score-icon-size);\n margin: var(--score-icon-margin);\n display: inline-block;\n}\n.lh-fraction__wrapper--pass .lh-fraction__content {\n color: var(--color-pass-secondary);\n}\n.lh-fraction__wrapper--pass .lh-fraction__background {\n background-color: var(--color-pass);\n}\n.lh-fraction__wrapper--pass .lh-fraction__content::before {\n background-color: var(--color-pass);\n border-radius: 50%;\n}\n.lh-fraction__wrapper--average .lh-fraction__content {\n color: var(--color-average-secondary);\n}\n.lh-fraction__wrapper--average .lh-fraction__background,\n.lh-fraction__wrapper--average .lh-fraction__content::before {\n background-color: var(--color-average);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content {\n color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__background {\n background-color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n.lh-fraction__wrapper--null .lh-fraction__content {\n color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__background {\n background-color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__content::before {\n border-radius: 50%;\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-700);\n}\n\n.lh-fraction__background {\n position: absolute;\n height: 100%;\n width: 100%;\n border-radius: calc(var(--gauge-circle-size) / 2);\n opacity: 0.1;\n z-index: -1;\n}\n\n.lh-fraction__content-wrapper {\n height: var(--gauge-circle-size);\n display: flex;\n align-items: center;\n}\n\n.lh-fraction__content {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: center;\n font-size: calc(0.3 * var(--gauge-circle-size));\n line-height: calc(0.4 * var(--gauge-circle-size));\n width: max-content;\n min-width: calc(1.5 * var(--gauge-circle-size));\n padding: calc(0.1 * var(--gauge-circle-size)) calc(0.2 * var(--gauge-circle-size));\n --score-icon-size: calc(0.21 * var(--gauge-circle-size));\n --score-icon-margin: 0 calc(0.15 * var(--gauge-circle-size)) 0 0;\n}\n\n.lh-gauge {\n stroke-linecap: round;\n width: var(--gauge-circle-size);\n height: var(--gauge-circle-size);\n}\n\n.lh-category .lh-gauge {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n.lh-gauge-base {\n opacity: 0.1;\n}\n\n.lh-gauge-arc {\n fill: none;\n transform-origin: 50% 50%;\n animation: load-gauge var(--transition-length) ease both;\n animation-delay: 250ms;\n}\n\n.lh-gauge__svg-wrapper {\n position: relative;\n height: var(--gauge-circle-size);\n}\n.lh-category .lh-gauge__svg-wrapper,\n.lh-category .lh-fraction__wrapper {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n/* The plugin badge overlay */\n.lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size);\n height: var(--plugin-badge-size);\n background-color: var(--plugin-badge-background-color);\n background-image: var(--plugin-icon-url);\n background-repeat: no-repeat;\n background-size: var(--plugin-icon-size);\n background-position: 58% 50%;\n content: \"\";\n position: absolute;\n right: -6px;\n bottom: 0px;\n display: block;\n z-index: 100;\n box-shadow: 0 0 4px rgba(0,0,0,.2);\n border-radius: 25%;\n}\n.lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size-big);\n height: var(--plugin-badge-size-big);\n}\n\n@keyframes load-gauge {\n from { stroke-dasharray: 0 352; }\n}\n\n.lh-gauge__percentage {\n width: 100%;\n height: var(--gauge-circle-size);\n position: absolute;\n font-family: var(--report-font-family-monospace);\n font-size: calc(var(--gauge-circle-size) * 0.34 + 1.3px);\n line-height: 0;\n text-align: center;\n top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 2);\n}\n\n.lh-category .lh-gauge__percentage {\n --gauge-circle-size: var(--gauge-circle-size-big);\n --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);\n}\n\n.lh-gauge__wrapper,\n.lh-fraction__wrapper {\n position: relative;\n display: flex;\n align-items: center;\n flex-direction: column;\n text-decoration: none;\n padding: var(--score-container-padding);\n\n --transition-length: 1s;\n\n /* Contain the layout style paint & layers during animation*/\n contain: content;\n will-change: opacity; /* Only using for layer promotion */\n}\n\n.lh-gauge__label,\n.lh-fraction__label {\n font-size: var(--gauge-label-font-size);\n font-weight: 500;\n line-height: var(--gauge-label-line-height);\n margin-top: 10px;\n text-align: center;\n color: var(--report-text-color);\n word-break: keep-all;\n}\n\n/* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */\n.lh-category .lh-gauge__label,\n.lh-category .lh-fraction__label {\n --gauge-label-font-size: var(--gauge-label-font-size-big);\n --gauge-label-line-height: var(--gauge-label-line-height-big);\n margin-top: 14px;\n}\n\n.lh-scores-header .lh-gauge__wrapper,\n.lh-scores-header .lh-fraction__wrapper,\n.lh-scores-header .lh-gauge--pwa__wrapper,\n.lh-sticky-header .lh-gauge__wrapper,\n.lh-sticky-header .lh-fraction__wrapper,\n.lh-sticky-header .lh-gauge--pwa__wrapper {\n width: var(--gauge-wrapper-width);\n}\n\n.lh-scorescale {\n display: inline-flex;\n\n gap: calc(var(--default-padding) * 4);\n margin: 16px auto 0 auto;\n font-size: var(--report-font-size-secondary);\n color: var(--color-gray-700);\n\n}\n\n.lh-scorescale-range {\n display: flex;\n align-items: center;\n font-family: var(--report-font-family-monospace);\n white-space: nowrap;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale {\n border: 0;\n display: flex;\n justify-content: center;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale-range {\n font-family: unset;\n font-size: 12px;\n}\n\n.lh-scorescale-wrap {\n display: contents;\n}\n\n/* Hide category score gauages if it's a single category report */\n.lh-header--solo-category .lh-scores-wrapper {\n display: none;\n}\n\n\n.lh-categories {\n width: 100%;\n}\n\n.lh-category {\n padding: var(--category-padding);\n max-width: var(--report-content-max-width);\n margin: 0 auto;\n\n --sticky-header-height: calc(var(--gauge-circle-size-sm) + var(--score-container-padding) * 2);\n --topbar-plus-sticky-header: calc(var(--topbar-height) + var(--sticky-header-height));\n scroll-margin-top: var(--topbar-plus-sticky-header);\n\n /* Faster recalc style & layout of the report. https://web.dev/content-visibility/ */\n content-visibility: auto;\n contain-intrinsic-size: 1000px;\n}\n\n.lh-category-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n.lh-category-wrapper:last-of-type {\n border-bottom: 0;\n}\n\n.lh-category-header {\n margin-bottom: var(--section-padding-vertical);\n}\n\n.lh-category-header .lh-score__gauge {\n max-width: 400px;\n width: auto;\n margin: 0px auto;\n}\n\n.lh-category-header__finalscreenshot {\n display: grid;\n grid-template: none / 1fr 1px 1fr;\n justify-items: center;\n align-items: center;\n gap: var(--report-line-height);\n min-height: 288px;\n margin-bottom: var(--default-padding);\n}\n\n.lh-final-ss-image {\n /* constrain the size of the image to not be too large */\n max-height: calc(var(--gauge-circle-size-big) * 2.8);\n max-width: calc(var(--gauge-circle-size-big) * 3.5);\n border: 1px solid var(--color-gray-200);\n padding: 4px;\n border-radius: 3px;\n display: block;\n}\n\n.lh-category-headercol--separator {\n background: var(--color-gray-200);\n width: 1px;\n height: var(--gauge-circle-size-big);\n}\n\n@media screen and (max-width: 780px) {\n .lh-category-header__finalscreenshot {\n grid-template: 1fr 1fr / none\n }\n .lh-category-headercol--separator {\n display: none;\n }\n}\n\n\n/* 964 fits the min-width of the filmstrip */\n@media screen and (max-width: 964px) {\n .lh-report {\n margin-left: 0;\n width: 100%;\n }\n}\n\n@media print {\n body {\n -webkit-print-color-adjust: exact; /* print background colors */\n }\n .lh-container {\n display: block;\n }\n .lh-report {\n margin-left: 0;\n padding-top: 0;\n }\n .lh-categories {\n margin-top: 0;\n }\n}\n\n.lh-table {\n position: relative;\n border-collapse: separate;\n border-spacing: 0;\n /* Can't assign padding to table, so shorten the width instead. */\n width: calc(100% - var(--audit-description-padding-left) - var(--stackpack-padding-horizontal));\n border: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-table thead th {\n position: sticky;\n top: calc(var(--topbar-plus-sticky-header) + 1em);\n z-index: 1;\n background-color: var(--report-background-color);\n border-bottom: 1px solid var(--report-border-color-secondary);\n font-weight: normal;\n color: var(--color-gray-600);\n /* See text-wrapping comment on .lh-container. */\n word-break: normal;\n}\n\n.lh-table hr {\n border-width: 0.5px;\n}\n\n.lh-row--even {\n background-color: var(--table-higlight-background-color);\n}\n.lh-row--hidden {\n display: none;\n}\n\n.lh-table th,\n.lh-table td {\n padding: var(--default-padding);\n}\n\n.lh-table tr {\n vertical-align: middle;\n}\n\n/* Looks unnecessary, but mostly for keeping the s left-aligned */\n.lh-table-column--text,\n.lh-table-column--source-location,\n.lh-table-column--url,\n/* .lh-table-column--thumbnail, */\n/* .lh-table-column--empty,*/\n.lh-table-column--code,\n.lh-table-column--node {\n text-align: left;\n}\n\n.lh-table-column--code {\n min-width: 100px;\n}\n\n.lh-table-column--bytes,\n.lh-table-column--timespanMs,\n.lh-table-column--ms,\n.lh-table-column--numeric {\n text-align: right;\n word-break: normal;\n}\n\n\n\n.lh-table .lh-table-column--thumbnail {\n width: var(--image-preview-size);\n}\n\n.lh-table-column--url {\n min-width: 250px;\n}\n\n.lh-table-column--text {\n min-width: 80px;\n}\n\n/* Keep columns narrow if they follow the URL column */\n/* 12% was determined to be a decent narrow width, but wide enough for column headings */\n.lh-table-column--url + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--ms,\n.lh-table-column--url + .lh-table-column--ms + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--timespanMs {\n width: 12%;\n}\n\n.lh-text__url-host {\n display: inline;\n}\n\n.lh-text__url-host {\n margin-left: calc(var(--report-font-size) / 2);\n opacity: 0.6;\n font-size: 90%\n}\n\n.lh-thumbnail {\n object-fit: cover;\n width: var(--image-preview-size);\n height: var(--image-preview-size);\n display: block;\n}\n\n.lh-unknown pre {\n overflow: scroll;\n border: solid 1px var(--color-gray-200);\n}\n\n.lh-text__url > a {\n color: inherit;\n text-decoration: none;\n}\n\n.lh-text__url > a:hover {\n text-decoration: underline dotted #999;\n}\n\n.lh-sub-item-row {\n margin-left: 20px;\n margin-bottom: 0;\n color: var(--color-gray-700);\n}\n.lh-sub-item-row td {\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 20px;\n}\n\n/* Chevron\n https://codepen.io/paulirish/pen/LmzEmK\n */\n.lh-chevron {\n --chevron-angle: 42deg;\n /* Edge doesn't support transform: rotate(calc(...)), so we define it here */\n --chevron-angle-right: -42deg;\n width: var(--chevron-size);\n height: var(--chevron-size);\n margin-top: calc((var(--report-line-height) - 12px) / 2);\n}\n\n.lh-chevron__lines {\n transition: transform 0.4s;\n transform: translateY(var(--report-line-height));\n}\n.lh-chevron__line {\n stroke: var(--chevron-line-stroke);\n stroke-width: var(--chevron-size);\n stroke-linecap: square;\n transform-origin: 50%;\n transform: rotate(var(--chevron-angle));\n transition: transform 300ms, stroke 300ms;\n}\n\n.lh-expandable-details .lh-chevron__line-right,\n.lh-expandable-details[open] .lh-chevron__line-left {\n transform: rotate(var(--chevron-angle-right));\n}\n\n.lh-expandable-details[open] .lh-chevron__line-right {\n transform: rotate(var(--chevron-angle));\n}\n\n\n.lh-expandable-details[open] .lh-chevron__lines {\n transform: translateY(calc(var(--chevron-size) * -1));\n}\n\n.lh-expandable-details[open] {\n animation: 300ms openDetails forwards;\n padding-bottom: var(--default-padding);\n}\n\n@keyframes openDetails {\n from {\n outline: 1px solid var(--report-background-color);\n }\n to {\n outline: 1px solid;\n box-shadow: 0 2px 4px rgba(0, 0, 0, .24);\n }\n}\n\n@media screen and (max-width: 780px) {\n /* no black outline if we're not confident the entire table can be displayed within bounds */\n .lh-expandable-details[open] {\n animation: none;\n }\n}\n\n.lh-expandable-details[open] summary, details.lh-clump > summary {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\ndetails.lh-clump[open] > summary {\n border-bottom-width: 0;\n}\n\n\n\ndetails .lh-clump-toggletext--hide,\ndetails[open] .lh-clump-toggletext--show { display: none; }\ndetails[open] .lh-clump-toggletext--hide { display: block;}\n\n\n/* Tooltip */\n.lh-tooltip-boundary {\n position: relative;\n}\n\n.lh-tooltip {\n position: absolute;\n display: none; /* Don't retain these layers when not needed */\n opacity: 0;\n background: #ffffff;\n white-space: pre-line; /* Render newlines in the text */\n min-width: 246px;\n max-width: 275px;\n padding: 15px;\n border-radius: 5px;\n text-align: initial;\n line-height: 1.4;\n}\n/* shrink tooltips to not be cutoff on left edge of narrow viewports\n 45vw is chosen to be ~= width of the left column of metrics\n*/\n@media screen and (max-width: 535px) {\n .lh-tooltip {\n min-width: 45vw;\n padding: 3vw;\n }\n}\n\n.lh-tooltip-boundary:hover .lh-tooltip {\n display: block;\n animation: fadeInTooltip 250ms;\n animation-fill-mode: forwards;\n animation-delay: 850ms;\n bottom: 100%;\n z-index: 1;\n will-change: opacity;\n right: 0;\n pointer-events: none;\n}\n\n.lh-tooltip::before {\n content: \"\";\n border: solid transparent;\n border-bottom-color: #fff;\n border-width: 10px;\n position: absolute;\n bottom: -20px;\n right: 6px;\n transform: rotate(180deg);\n pointer-events: none;\n}\n\n@keyframes fadeInTooltip {\n 0% { opacity: 0; }\n 75% { opacity: 1; }\n 100% { opacity: 1; filter: drop-shadow(1px 0px 1px #aaa) drop-shadow(0px 2px 4px hsla(206, 6%, 25%, 0.15)); pointer-events: auto; }\n}\n\n/* Element screenshot */\n.lh-element-screenshot {\n float: left;\n margin-right: 20px;\n}\n.lh-element-screenshot__content {\n overflow: hidden;\n min-width: 110px;\n display: flex;\n justify-content: center;\n background-color: var(--report-background-color);\n}\n.lh-element-screenshot__image {\n position: relative;\n /* Set by ElementScreenshotRenderer.installFullPageScreenshotCssVariable */\n background-image: var(--element-screenshot-url);\n outline: 2px solid #777;\n background-color: white;\n background-repeat: no-repeat;\n}\n.lh-element-screenshot__mask {\n position: absolute;\n background: #555;\n opacity: 0.8;\n}\n.lh-element-screenshot__element-marker {\n position: absolute;\n outline: 2px solid var(--color-lime-400);\n}\n.lh-element-screenshot__overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2000; /* .lh-topbar is 1000 */\n background: var(--screenshot-overlay-background);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n\n.lh-element-screenshot__overlay .lh-element-screenshot {\n margin-right: 0; /* clearing margin used in thumbnail case */\n outline: 1px solid var(--color-gray-700);\n}\n\n.lh-screenshot-overlay--enabled .lh-element-screenshot {\n cursor: zoom-out;\n}\n.lh-screenshot-overlay--enabled .lh-node .lh-element-screenshot {\n cursor: zoom-in;\n}\n\n\n.lh-meta__items {\n --meta-icon-size: calc(var(--report-icon-size) * 0.667);\n padding: var(--default-padding);\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n background-color: var(--env-item-background-color);\n border-radius: 3px;\n margin: 0 0 var(--default-padding) 0;\n font-size: 12px;\n column-gap: var(--default-padding);\n color: var(--color-gray-700);\n}\n\n.lh-meta__item {\n display: block;\n list-style-type: none;\n position: relative;\n padding: 0 0 0 calc(var(--meta-icon-size) + var(--default-padding) * 2);\n cursor: unset; /* disable pointer cursor from report-icon */\n}\n\n.lh-meta__item.lh-tooltip-boundary {\n text-decoration: dotted underline var(--color-gray-500);\n cursor: help;\n}\n\n.lh-meta__item.lh-report-icon::before {\n position: absolute;\n left: var(--default-padding);\n width: var(--meta-icon-size);\n height: var(--meta-icon-size);\n}\n\n.lh-meta__item.lh-report-icon:hover::before {\n opacity: 0.7;\n}\n\n.lh-meta__item .lh-tooltip {\n color: var(--color-gray-800);\n}\n\n.lh-meta__item .lh-tooltip::before {\n right: auto; /* Set the tooltip arrow to the leftside */\n left: 6px;\n}\n\n/* Change the grid for narrow viewport. */\n@media screen and (max-width: 640px) {\n .lh-meta__items {\n grid-template-columns: 1fr 1fr;\n }\n}\n@media screen and (max-width: 535px) {\n .lh-meta__items {\n display: block;\n }\n}\n\n\n/*# sourceURL=report-styles.css */\n"); el0.append(el1); return el0; } diff --git a/report/renderer/details-renderer.js b/report/renderer/details-renderer.js index b1ef590d9f77..8bd23b5080cd 100644 --- a/report/renderer/details-renderer.js +++ b/report/renderer/details-renderer.js @@ -356,15 +356,27 @@ export class DetailsRenderer { * expand into multiple rows, if there is a subItemsHeading. * @param {TableItem} item * @param {LH.Audit.Details.TableColumnHeading[]} headings + * @param {string | undefined} defaultSubItemsLabel */ - _renderTableRowsFromItem(item, headings) { + _renderTableRowsFromItem(item, headings, defaultSubItemsLabel) { const fragment = this._dom.createFragment(); fragment.append(this._renderTableRow(item, headings)); if (!item.subItems) return fragment; const subItemsHeadings = headings.map(this._getDerivedSubItemsHeading); - if (!subItemsHeadings.some(Boolean)) return fragment; + if (!subItemsHeadings.some(Boolean) || !item.subItems.items.length) return fragment; + + const label = item.subItems.label ?? defaultSubItemsLabel; + if (label) { + const rowEl = this._dom.createElement('tr'); + const tdEl = this._dom.createChildOf(rowEl, 'td'); + tdEl.setAttribute('colspan', headings.length.toString()); + tdEl.append(this._renderText(label)); + tdEl.append(this._dom.createElement('hr')); + rowEl.classList.add('lh-sub-item-row'); + fragment.append(rowEl); + } for (const subItem of item.subItems.items) { const rowEl = this._renderTableRow(subItem, subItemsHeadings); @@ -376,7 +388,7 @@ export class DetailsRenderer { } /** - * @param {{headings: TableColumnHeading[], items: TableItem[]}} details + * @param {{headings: TableColumnHeading[], items: TableItem[], subItemsLabel?: string}} details * @return {Element} */ _renderTable(details) { @@ -397,7 +409,8 @@ export class DetailsRenderer { const tbodyElem = this._dom.createChildOf(tableElem, 'tbody'); let even = true; for (const item of details.items) { - const rowsFragment = this._renderTableRowsFromItem(item, details.headings); + const rowsFragment = + this._renderTableRowsFromItem(item, details.headings, details.subItemsLabel); for (const rowEl of this._dom.findAll('tr', rowsFragment)) { // For zebra styling. rowEl.classList.add(even ? 'lh-row--even' : 'lh-row--odd'); diff --git a/report/test/renderer/report-ui-features-test.js b/report/test/renderer/report-ui-features-test.js index d023ffdb50a3..7a54b4ab7cc5 100644 --- a/report/test/renderer/report-ui-features-test.js +++ b/report/test/renderer/report-ui-features-test.js @@ -235,12 +235,15 @@ describe('ReportUIFeatures', () => { const initialExpected = [ '/script1.js(www.cdn.com)24.0 KiB8.8 KiB', + 'Module', '10.0 KiB0.0 KiB', '20.0 KiB0.0 KiB', '/script2.js(www.example.com)24.0 KiB8.8 KiB', + 'Module', '30.0 KiB0.0 KiB', '40.0 KiB0.0 KiB', '/script3.js(www.notexample.com)24.0 KiB8.8 KiB', + 'Module', '50.0 KiB0.0 KiB', '60.0 KiB0.0 KiB', ]; @@ -249,6 +252,7 @@ describe('ReportUIFeatures', () => { filterCheckbox.click(); expect(getRowIdentifiers()).toEqual([ '/script2.js(www.example.com)24.0 KiB8.8 KiB', + 'Module', '30.0 KiB0.0 KiB', '40.0 KiB0.0 KiB', ]); diff --git a/shared/localization/locales/en-US.json b/shared/localization/locales/en-US.json index 53f10ccd2dc4..fb38fa5d5367 100644 --- a/shared/localization/locales/en-US.json +++ b/shared/localization/locales/en-US.json @@ -155,6 +155,9 @@ "core/audits/accessibility/axe-audit.js | failingElementsHeader": { "message": "Failing Elements" }, + "core/audits/accessibility/axe-audit.js | relatedElementsHeader": { + "message": "Related Elements" + }, "core/audits/accessibility/button-name.js | description": { "message": "When a button doesn't have an accessible name, screen readers announce it as \"button\", making it unusable for users who rely on screen readers. [Learn how to make buttons more accessible](https://dequeuniversity.com/rules/axe/4.4/button-name)." }, @@ -173,6 +176,9 @@ "core/audits/accessibility/bypass.js | title": { "message": "The page contains a heading, skip link, or landmark region" }, + "core/audits/accessibility/color-contrast.js | backgroundElementHeader": { + "message": "Background Element" + }, "core/audits/accessibility/color-contrast.js | description": { "message": "Low-contrast text is difficult or impossible for many users to read. [Learn how to provide sufficient color contrast](https://dequeuniversity.com/rules/axe/4.4/color-contrast)." }, @@ -224,6 +230,9 @@ "core/audits/accessibility/duplicate-id-aria.js | failureTitle": { "message": "ARIA IDs are not unique" }, + "core/audits/accessibility/duplicate-id-aria.js | relatedElementsHeader": { + "message": "Duplicate Elements" + }, "core/audits/accessibility/duplicate-id-aria.js | title": { "message": "ARIA IDs are unique" }, @@ -2339,6 +2348,9 @@ "core/lib/i18n/i18n.js | mediaResourceType": { "message": "Media" }, + "core/lib/i18n/i18n.js | module": { + "message": "Module" + }, "core/lib/i18n/i18n.js | ms": { "message": "{timeInMs, number, milliseconds} ms" }, @@ -2348,6 +2360,9 @@ "core/lib/i18n/i18n.js | otherResourceType": { "message": "Other" }, + "core/lib/i18n/i18n.js | reasons": { + "message": "Reasons" + }, "core/lib/i18n/i18n.js | scriptResourceType": { "message": "Script" }, diff --git a/shared/localization/locales/en-XL.json b/shared/localization/locales/en-XL.json index 84d637acf0e0..82eb267cfa07 100644 --- a/shared/localization/locales/en-XL.json +++ b/shared/localization/locales/en-XL.json @@ -155,6 +155,9 @@ "core/audits/accessibility/axe-audit.js | failingElementsHeader": { "message": "F̂áîĺîńĝ Él̂ém̂én̂t́ŝ" }, + "core/audits/accessibility/axe-audit.js | relatedElementsHeader": { + "message": "R̂él̂át̂éd̂ Él̂ém̂én̂t́ŝ" + }, "core/audits/accessibility/button-name.js | description": { "message": "Ŵh́êń â b́ût́t̂ón̂ d́ôéŝń't̂ h́âv́ê án̂ áĉćêśŝíb̂ĺê ńâḿê, śĉŕêén̂ ŕêád̂ér̂ś âńn̂óûńĉé ît́ âś \"b̂út̂t́ôń\", m̂ák̂ín̂ǵ ît́ ûńûśâb́l̂é f̂ór̂ úŝér̂ś ŵh́ô ŕêĺŷ ón̂ śĉŕêén̂ ŕêád̂ér̂ś. [L̂éâŕn̂ h́ôẃ t̂ó m̂ák̂é b̂út̂t́ôńŝ ḿôŕê áĉćêśŝíb̂ĺê](https://dequeuniversity.com/rules/axe/4.4/button-name)." }, @@ -173,6 +176,9 @@ "core/audits/accessibility/bypass.js | title": { "message": "T̂h́ê ṕâǵê ćôńt̂áîńŝ á ĥéâd́îńĝ, śk̂íp̂ ĺîńk̂, ór̂ ĺâńd̂ḿâŕk̂ ŕêǵîón̂" }, + "core/audits/accessibility/color-contrast.js | backgroundElementHeader": { + "message": "B̂áĉḱĝŕôún̂d́ Êĺêḿêńt̂" + }, "core/audits/accessibility/color-contrast.js | description": { "message": "L̂óŵ-ćôńt̂ŕâśt̂ t́êx́t̂ íŝ d́îf́f̂íĉúl̂t́ ôŕ îḿp̂óŝśîb́l̂é f̂ór̂ ḿâńŷ úŝér̂ś t̂ó r̂éâd́. [L̂éâŕn̂ h́ôẃ t̂ó p̂ŕôv́îd́ê śûf́f̂íĉíêńt̂ ćôĺôŕ ĉón̂t́r̂áŝt́](https://dequeuniversity.com/rules/axe/4.4/color-contrast)." }, @@ -224,6 +230,9 @@ "core/audits/accessibility/duplicate-id-aria.js | failureTitle": { "message": "ÂŔÎÁ ÎD́ŝ ár̂é n̂ót̂ ún̂íq̂úê" }, + "core/audits/accessibility/duplicate-id-aria.js | relatedElementsHeader": { + "message": "D̂úp̂ĺîćât́ê Él̂ém̂én̂t́ŝ" + }, "core/audits/accessibility/duplicate-id-aria.js | title": { "message": "ÂŔÎÁ ÎD́ŝ ár̂é ûńîq́ûé" }, @@ -2339,6 +2348,9 @@ "core/lib/i18n/i18n.js | mediaResourceType": { "message": "M̂éd̂íâ" }, + "core/lib/i18n/i18n.js | module": { + "message": "M̂ód̂úl̂é" + }, "core/lib/i18n/i18n.js | ms": { "message": "{timeInMs, number, milliseconds} m̂ś" }, @@ -2348,6 +2360,9 @@ "core/lib/i18n/i18n.js | otherResourceType": { "message": "Ôt́ĥér̂" }, + "core/lib/i18n/i18n.js | reasons": { + "message": "R̂éâśôńŝ" + }, "core/lib/i18n/i18n.js | scriptResourceType": { "message": "Ŝćr̂íp̂t́" }, diff --git a/types/lhr/audit-details.d.ts b/types/lhr/audit-details.d.ts index d32296375418..3c2b218d8557 100644 --- a/types/lhr/audit-details.d.ts +++ b/types/lhr/audit-details.d.ts @@ -68,6 +68,7 @@ declare module Details { overallSavingsMs: number; overallSavingsBytes?: number; headings: TableColumnHeading[]; + subItemsLabel?: string | IcuMessage; items: OpportunityItem[]; debugData?: DebugData; } @@ -107,6 +108,7 @@ declare module Details { interface Table { type: 'table'; headings: TableColumnHeading[]; + subItemsLabel?: string | IcuMessage; items: TableItem[]; summary?: { wastedMs?: number; @@ -119,6 +121,7 @@ declare module Details { interface TableSubItems { type: 'subitems'; items: TableItem[]; + label?: string | IcuMessage; } /**