Repository: GoogleChrome/lighthouse
Pull request #9149 (2019-06-07 18:32:48)

report: create naming convention for css variables
The only visual change should be this layout fix (see that bad chevron):

Before
![image](https://user-images.githubusercontent.com/4071474/59125511-67309100-8917-11e9-9e49-d895e162ae95.png)
After
![image](https://user-images.githubusercontent.com/4071474/59125519-6dbf0880-8917-11e9-9dac-f22400277628.png)


Open to discussion. Note, I will follow up with sorting. Some stuff to start us off:

1. First off, thoughts on the naming convention?
2. For variables that affect the entire report, what "component" name should be used? We kinda used `body` before, but I don't like that because places like PSI / DevTools don't provide the entire body to the report. I'm thinking either `report` or `default`.
3. Should _everything_ be prepended with `lh-`?
4. Should we be using only Material Design colors?
5. Most of these variables were simple to rename. I had trouble with some, which I marked.

patrickhulce: Oh and this looks pre-existing but the large amount of padding on our failure audits jumped out as unusual to me when doing a quick visual check

![image](https://user-images.githubusercontent.com/2301202/59217703-2be6ca00-8b84-11e9-8226-092848e96573.png)

Hoten: > Oh and this looks pre-existing but the large amount of padding on our failure audits jumped out as unusual to me when doing a quick visual check

Confirmed it exists in a different PR's now deployment, so yeah pre-existing.

![image](https://user-images.githubusercontent.com/4071474/59218048-2bdece00-8b74-11e9-9eec-fd6c1b905104.png)

Empty display text wants some space too.
Hoten: > TODOs are followups?

Would like to resolve some of them here. The ones that just say `TODO: rename` I couldn't figure out a good name for them, any ideas?

also, if we don't want to prefix all vars with `lh-` I'll remove the ones that do have it.
Hoten: ok, another visual change made its way in. we were not changing the cheveron color in dark theme

before

![image](https://user-images.githubusercontent.com/4071474/59316326-c02f5a80-8c72-11e9-880b-7a0dfb0a11fa.png)


after

![image](https://user-images.githubusercontent.com/4071474/59316320-b9a0e300-8c72-11e9-89cb-c7353bb4affc.png)


Patch:
diff --git a/.appveyor.yml b/.appveyor.yml
index 71fa74955..f6e6cf6b9 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -21,7 +21,7 @@ install:
 
 before_test:
   - set "PATH=C:\MinGW\msys\1.0\bin;%PATH%"
-  - set "LIGHTHOUSE_CHROMIUM_PATH=%CD%\chrome-win32\chrome.exe"
+  - set "CHROME_PATH=%CD%\chrome-win32\chrome.exe"
   - bash ./lighthouse-core/scripts/download-chrome.sh
   - yarn build-all
 
diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md
index ea0f5dc98..098af3840 100644
--- a/.github/ISSUE_TEMPLATE/Bug_report.md
+++ b/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -10,6 +10,8 @@ about: Report something working incorrectly
 
 #### Provide the steps to reproduce
 1. Run LH on <affected url>
+  
+<!-- If your page is only local, or is liable to change, consider uploading a repro so that we can more easily debug the problem. Some services that will help are: https://jsbin.com/, https://surge.sh/ -->
 
 #### What is the current behavior?
 
diff --git a/docs/understanding-results.md b/docs/understanding-results.md
index a98ec06f7..c753e8e1d 100644
--- a/docs/understanding-results.md
+++ b/docs/understanding-results.md
@@ -158,7 +158,7 @@ An array containing the different categories, their scores, and the results of t
 | id | `string` | The string identifier of the category. |
 | title | `string` | The human-friendly display name of the category. |
 | description | `string` | A brief description of the purpose of the category, supports markdown links. |
-| score | `string` | The overall score of the category, the weighted average of all its audits. |
+| score | `number` | The overall score of the category, the weighted average of all its audits. |
 | auditRefs | `AuditEntry[]` | An array of all the audit results in the category. |
 
 ### AuditEntry Properties
diff --git a/lighthouse-core/audits/font-display.js b/lighthouse-core/audits/font-display.js
index 52f0c1c35..9f273a0a2 100644
--- a/lighthouse-core/audits/font-display.js
+++ b/lighthouse-core/audits/font-display.js
@@ -24,6 +24,9 @@ const UIStrings = {
     'Leverage the font-display CSS feature to ensure text is user-visible while ' +
     'webfonts are loading. ' +
     '[Learn more](https://developers.google.com/web/updates/2016/02/font-display).',
+  /** A warning message that is shown when Lighthouse couldn't automatically check some of the page's fonts and that the user will need to manually check it. */
+  undeclaredFontURLWarning: 'Lighthouse was unable to automatically check the font-display value ' +
+    'for the following URL: {fontURL}.',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
@@ -44,10 +47,13 @@ class FontDisplay extends Audit {
 
   /**
    * @param {LH.Artifacts} artifacts
+   * @return {{passingURLs: Set<string>, failingURLs: Set<string>}}
    */
-  static findPassingFontDisplayDeclarations(artifacts) {
+  static findFontDisplayDeclarations(artifacts) {
     /** @type {Set<string>} */
     const passingURLs = new Set();
+    /** @type {Set<string>} */
+    const failingURLs = new Set();
 
     // Go through all the stylesheets to find all @font-face declarations
     for (const stylesheet of artifacts.CSSUsage.stylesheets) {
@@ -57,20 +63,18 @@ class FontDisplay extends Audit {
       const fontFaceDeclarations = newlinesStripped.match(/@font-face\s*{(.*?)}/g) || [];
       // Go through all the @font-face declarations to find a declared `font-display: ` property
       for (const declaration of fontFaceDeclarations) {
-        // Find the font-display value by matching a single token, optionally surrounded by whitespace,
-        // followed either by a semicolon or the end of a block.
-        const rawFontDisplay = declaration.match(/font-display\s*:\s*(\w+)\s*(;|\})/);
-        // If they didn't have a font-display property, it's the default, and it's failing; bail
-        if (!rawFontDisplay) continue;
-        // If they don't have one of the passing font-display values, it's failing; bail
-        const hasPassingFontDisplay = PASSING_FONT_DISPLAY_REGEX.test(rawFontDisplay[1]);
-        if (!hasPassingFontDisplay) continue;
-
-        // If it's passing, we'll try to find the URL it's referencing.
+        // We'll try to find the URL it's referencing.
         const rawFontURLs = declaration.match(CSS_URL_GLOBAL_REGEX);
         // If no URLs, we can't really do anything; bail
         if (!rawFontURLs) continue;
+        // Find the font-display value by matching a single token, optionally surrounded by whitespace,
+        // followed either by a semicolon or the end of a block.
+        const fontDisplayMatch = declaration.match(/font-display\s*:\s*(\w+)\s*(;|\})/);
+        const rawFontDisplay = (fontDisplayMatch && fontDisplayMatch[1]) || '';
+        const hasPassingFontDisplay = PASSING_FONT_DISPLAY_REGEX.test(rawFontDisplay);
+        const targetURLSet = hasPassingFontDisplay ? passingURLs : failingURLs;
 
+        // Finally convert the raw font URLs to the absolute URLs and add them to the set.
         const relativeURLs = rawFontURLs
           // @ts-ignore - guaranteed to match from previous regex, pull URL group out
           .map(s => s.match(CSS_URL_REGEX)[1].trim())
@@ -82,13 +86,14 @@ class FontDisplay extends Audit {
 
             return s;
           });
-        // Convert the relative CSS URL to an absolute URL and add it to the passing set
+
+        // Convert the relative CSS URL to an absolute URL and add it to the target set.
         for (const relativeURL of relativeURLs) {
           try {
             const relativeRoot = URL.isValid(stylesheet.header.sourceURL) ?
               stylesheet.header.sourceURL : artifacts.URL.finalUrl;
             const absoluteURL = new URL(relativeURL, relativeRoot);
-            passingURLs.add(absoluteURL.href);
+            targetURLSet.add(absoluteURL.href);
           } catch (err) {
             Sentry.captureException(err, {tags: {audit: this.meta.id}});
           }
@@ -96,7 +101,7 @@ class FontDisplay extends Audit {
       }
     }
 
-    return passingURLs;
+    return {passingURLs, failingURLs};
   }
 
   /**
@@ -107,16 +112,24 @@ class FontDisplay extends Audit {
   static async audit(artifacts, context) {
     const devtoolsLogs = artifacts.devtoolsLogs[this.DEFAULT_PASS];
     const networkRecords = await NetworkRecords.request(devtoolsLogs, context);
-    const passingFontURLs = FontDisplay.findPassingFontDisplayDeclarations(artifacts);
+    const {passingURLs, failingURLs} = FontDisplay.findFontDisplayDeclarations(artifacts);
+    /** @type {Array<string>} */
+    const warningURLs = [];
 
     const results = networkRecords
       // Find all fonts...
       .filter(record => record.resourceType === 'Font')
-      // ...that don't have a passing font-display value
-      .filter(record => !passingFontURLs.has(record.url))
       // ...and that aren't data URLs, the blocking concern doesn't really apply
       .filter(record => !/^data:/.test(record.url))
       .filter(record => !/^blob:/.test(record.url))
+      // ...that have a failing font-display value
+      .filter(record => {
+        // Failing URLs should be considered.
+        if (failingURLs.has(record.url)) return true;
+        // Everything else shouldn't be, but we should warn if we don't recognize the URL at all.
+        if (!passingURLs.has(record.url)) warningURLs.push(record.url);
+        return false;
+      })
       .map(record => {
         // In reality the end time should be calculated with paint time included
         // all browsers wait 3000ms to block text so we make sure 3000 is our max wasted time
@@ -139,6 +152,7 @@ class FontDisplay extends Audit {
     return {
       score: Number(results.length === 0),
       details,
+      warnings: warningURLs.map(fontURL => str_(UIStrings.undeclaredFontURLWarning, {fontURL})),
     };
   }
 }
diff --git a/lighthouse-core/gather/gather-runner.js b/lighthouse-core/gather/gather-runner.js
index c1d50c80f..deb8d4611 100644
--- a/lighthouse-core/gather/gather-runner.js
+++ b/lighthouse-core/gather/gather-runner.js
@@ -167,6 +167,37 @@ class GatherRunner {
     }
   }
 
+  /**
+   * Returns an error if we ended up on the `chrome-error` page and all other requests failed.
+   * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
+   * @return {LH.LighthouseError|undefined}
+   */
+  static getInterstitialError(networkRecords) {
+    const interstitialRequest = networkRecords
+      .find(record => record.documentURL.startsWith('chrome-error://'));
+    // If the page didn't end up on a chrome interstitial, there's no error here.
+    if (!interstitialRequest) return undefined;
+
+    const pageNetworkRecords = networkRecords
+      .filter(record => !URL.NON_NETWORK_PROTOCOLS.includes(record.protocol) &&
+        !record.documentURL.startsWith('chrome-error://'));
+    // If none of the requests failed, there's no error here.
+    // We don't expect that this case could ever occur, but better safe than sorry.
+    // Note also that in cases of redirects, the initial requests could succeed and we still end up
+    // on the error interstitial page.
+    if (!pageNetworkRecords.some(record => record.failed)) return undefined;
+
+    // If a request failed with the `net::ERR_CERT_*` collection of errors, then it's a security issue.
+    const insecureRequest = pageNetworkRecords.find(record =>
+      record.failed && record.localizedFailDescription.startsWith('net::ERR_CERT'));
+    if (insecureRequest) {
+      return new LHError(LHError.errors.INSECURE_DOCUMENT_REQUEST, {securityMessages:
+        insecureRequest.localizedFailDescription});
+    }
+
+    return new LHError(LHError.errors.CHROME_INTERSTITIAL_ERROR);
+  }
+
   /**
    * Returns an error if the page load should be considered failed, e.g. from a
    * main document request failure, a security issue, etc.
@@ -177,10 +208,14 @@ class GatherRunner {
    */
   static getPageLoadError(passContext, loadData, navigationError) {
     const networkError = GatherRunner.getNetworkError(passContext.url, loadData.networkRecords);
+    const interstitialError = GatherRunner.getInterstitialError(loadData.networkRecords);
 
-    //  If the driver was offline, the load will fail without offline support. Ignore this case.
+    // If the driver was offline, the load will fail without offline support. Ignore this case.
     if (!passContext.driver.online) return;
 
+    // We want to special-case the interstitial beyond FAILED_DOCUMENT_REQUEST. See https://github.com/GoogleChrome/lighthouse/pull/8865#issuecomment-497507618
+    if (interstitialError) return interstitialError;
+
     // Network errors are usually the most specific and provide the best reason for why the page failed to load.
     // Prefer networkError over navigationError.
     // Example: `DNS_FAILURE` is better than `NO_FCP`.
diff --git a/lighthouse-core/lib/i18n/en-US.json b/lighthouse-core/lib/i18n/en-US.json
index b4d573c3d..91c6886af 100644
--- a/lighthouse-core/lib/i18n/en-US.json
+++ b/lighthouse-core/lib/i18n/en-US.json
@@ -651,6 +651,10 @@
     "message": "All text remains visible during webfont loads",
     "description": "Title of a diagnostic audit that provides detail on if all the text on a webpage was visible while the page was loading its webfonts. This descriptive title is shown to users when the amount is acceptable and no user action is required."
   },
+  "lighthouse-core/audits/font-display.js | undeclaredFontURLWarning": {
+    "message": "Lighthouse was unable to automatically check the font-display value for the following URL: {fontURL}.",
+    "description": "A warning message that is shown when Lighthouse couldn't automatically check some of the page's fonts and that the user will need to manually check it."
+  },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | description": {
     "message": "A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).",
     "description": "Description of a Lighthouse audit that tells the user *why* they need to load fast enough on mobile networks. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."
@@ -1363,6 +1367,10 @@
     "message": "The URL you have provided does not have a valid security certificate. {securityMessages}",
     "description": "Error message explaining that the security certificate of the page Lighthouse observed was invalid, so the URL cannot be accessed. securityMessages will be replaced with one or more strings from the browser explaining what was insecure about the page load."
   },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailedInterstitial": {
+    "message": "Chrome prevented page load with an interstitial. Make sure you are testing the correct URL and that the server is properly responding to all requests.",
+    "description": "Error message explaining that Chrome prevented the page from loading and displayed an interstitial screen instead, so the URL cannot be accessed."
+  },
   "lighthouse-core/lib/lh-error.js | pageLoadFailedWithDetails": {
     "message": "Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: {errorDetails})",
     "description": "Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."
diff --git a/lighthouse-core/lib/i18n/locales.js b/lighthouse-core/lib/i18n/locales.js
index 273f778b7..37f76548c 100644
--- a/lighthouse-core/lib/i18n/locales.js
+++ b/lighthouse-core/lib/i18n/locales.js
@@ -40,6 +40,31 @@ const locales = {
   'el': require('./locales/el.json'),
   'en-XA': require('./locales/en-XA.json'), // psuedolocalization
   'es': require('./locales/es.json'),
+  'es-419': require('./locales/es-419.json'),
+  // Aliases of es-419: https://raw.githubusercontent.com/unicode-cldr/cldr-core/master/supplemental/parentLocales.json
+  'es-AR': require('./locales/es-419.json'),
+  'es-BO': require('./locales/es-419.json'),
+  'es-BR': require('./locales/es-419.json'),
+  'es-BZ': require('./locales/es-419.json'),
+  'es-CL': require('./locales/es-419.json'),
+  'es-CO': require('./locales/es-419.json'),
+  'es-CR': require('./locales/es-419.json'),
+  'es-CU': require('./locales/es-419.json'),
+  'es-DO': require('./locales/es-419.json'),
+  'es-EC': require('./locales/es-419.json'),
+  'es-GT': require('./locales/es-419.json'),
+  'es-HN': require('./locales/es-419.json'),
+  'es-MX': require('./locales/es-419.json'),
+  'es-NI': require('./locales/es-419.json'),
+  'es-PA': require('./locales/es-419.json'),
+  'es-PE': require('./locales/es-419.json'),
+  'es-PR': require('./locales/es-419.json'),
+  'es-PY': require('./locales/es-419.json'),
+  'es-SV': require('./locales/es-419.json'),
+  'es-US': require('./locales/es-419.json'),
+  'es-UY': require('./locales/es-419.json'),
+  'es-VE': require('./locales/es-419.json'),
+
   'fi': require('./locales/fi.json'),
   'fil': require('./locales/fil.json'),
   'fr': require('./locales/fr.json'), // fr-CH identical, so it falls back into fr
diff --git a/lighthouse-core/lib/i18n/locales/es-419.json b/lighthouse-core/lib/i18n/locales/es-419.json
new file mode 100644
index 000000000..2e594f377
--- /dev/null
+++ b/lighthouse-core/lib/i18n/locales/es-419.json
@@ -0,0 +1,1064 @@
+{
+  "lighthouse-core/audits/accessibility/accesskeys.js | description": {
+    "message": "Las claves de acceso permiten a los usuarios dirigirse rápidamente a una parte concreta de la página. Para facilitar una navegación correcta, las claves de acceso deben ser únicas. [Más información](https://dequeuniversity.com/rules/axe/3.1/accesskeys?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/accesskeys.js | failureTitle": {
+    "message": "Los valores de \"[accesskey]\" no son únicos"
+  },
+  "lighthouse-core/audits/accessibility/accesskeys.js | title": {
+    "message": "Los valores de \"[accesskey]\" son únicos"
+  },
+  "lighthouse-core/audits/accessibility/aria-allowed-attr.js | description": {
+    "message": "Cada función de ARIA admite un subconjunto determinado de atributos \"aria-*\". Si no coinciden, se invalidarán los atributos \"aria-*\". [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-allowed-attr.js | failureTitle": {
+    "message": "Los atributos \"[aria-*]\" no se corresponden con sus funciones"
+  },
+  "lighthouse-core/audits/accessibility/aria-allowed-attr.js | title": {
+    "message": "Los atributos \"[aria-*]\" se corresponden con sus funciones"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-attr.js | description": {
+    "message": "Algunas funciones de ARIA incluyen atributos obligatorios que describen el estado del elemento a los lectores de pantalla. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-required-attr?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-attr.js | failureTitle": {
+    "message": "\"[role]\" no incluyen todos los atributos \"[aria-*]\" necesarios"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-attr.js | title": {
+    "message": "\"[role]\" incluyen todos los atributos \"[aria-*]\" necesarios"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-children.js | description": {
+    "message": "Algunas funciones principales de ARIA deben contener funciones secundarias específicas para llevar a cabo las funciones de accesibilidad correspondientes. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-required-children?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-children.js | failureTitle": {
+    "message": "Faltan elementos con atributos \"[role]\" que requieren otros atributos \"[role]\" secundarios específicos."
+  },
+  "lighthouse-core/audits/accessibility/aria-required-children.js | title": {
+    "message": "Se incluyeron los elementos con atributos \"[role]\" que requieren otros atributos \"[role]\" secundarios específicos"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-parent.js | description": {
+    "message": "Algunas funciones secundarias de ARIA se deben incluir dentro de funciones principales concretas para poder llevar a cabo las funciones de accesibilidad correspondientes. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-required-parent?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-parent.js | failureTitle": {
+    "message": "Los atributos \"[role]\" no se incluyen en los elementos principales necesarios"
+  },
+  "lighthouse-core/audits/accessibility/aria-required-parent.js | title": {
+    "message": "Los atributos \"[role]\" están incluidos en los elementos principales correspondientes"
+  },
+  "lighthouse-core/audits/accessibility/aria-roles.js | description": {
+    "message": "Las funciones de ARIA deben tener valores válidos para realizar las funciones de accesibilidad correspondientes. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-roles.js | failureTitle": {
+    "message": "Los valores de \"[role]\" no son válidos"
+  },
+  "lighthouse-core/audits/accessibility/aria-roles.js | title": {
+    "message": "Los valores de \"[role]\" son válidos"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr-value.js | description": {
+    "message": "Las tecnologías asistivas, como lectores de pantalla, no pueden interpretar atributos ARIA con valores no válidos. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr-value?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr-value.js | failureTitle": {
+    "message": "Los atributos \"[aria-*]\" no tienen valores válidos"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr-value.js | title": {
+    "message": "Los atributos \"[aria-*]\" tienen un valor válido"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr.js | description": {
+    "message": "Las tecnologías de asistencia, como los lectores de pantalla, no pueden interpretar los atributos ARIA cuyos valores no sean válidos. [Más información](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr.js | failureTitle": {
+    "message": "Los atributos \"[aria-*]\" no son válidos o no están bien escritos"
+  },
+  "lighthouse-core/audits/accessibility/aria-valid-attr.js | title": {
+    "message": "Los atributos \"[aria-*]\" son válidos y están bien escritos"
+  },
+  "lighthouse-core/audits/accessibility/audio-caption.js | description": {
+    "message": "Los subtítulos permiten que los elementos de audio sean útiles para usuarios sordos o con problemas de audición, dado que proporcionan información fundamental, como identificar quién habla, lo que dicen y otros sonidos no verbales. [Más información](https://dequeuniversity.com/rules/axe/3.1/audio-caption?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/audio-caption.js | failureTitle": {
+    "message": "Falta un elemento \"<track>\" con \"[kind=\"captions\"]\" en los elementos \"<audio>\"."
+  },
+  "lighthouse-core/audits/accessibility/audio-caption.js | title": {
+    "message": "Los elementos \"<audio>\" contienen un elemento \"<track>\" con \"[kind=\"captions\"]\""
+  },
+  "lighthouse-core/audits/accessibility/axe-audit.js | failingElementsHeader": {
+    "message": "Elementos con errores"
+  },
+  "lighthouse-core/audits/accessibility/button-name.js | description": {
+    "message": "Si un botón no tiene un nombre accesible, los lectores de pantalla lo leerán en voz alta como \"botón\", por lo que resultan inservibles para los usuarios que necesitan usar lectores de pantalla para navegar. [Más información](https://dequeuniversity.com/rules/axe/3.1/button-name?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/button-name.js | failureTitle": {
+    "message": "Los botones no tienen nombres accesibles"
+  },
+  "lighthouse-core/audits/accessibility/button-name.js | title": {
+    "message": "Los botones tienen nombres accesibles"
+  },
+  "lighthouse-core/audits/accessibility/bypass.js | description": {
+    "message": "Incluir maneras de omitir el contenido repetitivo permite a los usuarios navegar por la página de forma más eficaz. [Más información](https://dequeuniversity.com/rules/axe/3.1/bypass?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/bypass.js | failureTitle": {
+    "message": "La página no contiene ningún título, vínculo de omisión ni región de punto de referencia"
+  },
+  "lighthouse-core/audits/accessibility/bypass.js | title": {
+    "message": "La página contiene un título, un vínculo de omisión o una región de punto de referencia"
+  },
+  "lighthouse-core/audits/accessibility/color-contrast.js | description": {
+    "message": "Los textos con poco contraste resultan difíciles o imposibles de leer para muchos usuarios. [Más información](https://dequeuniversity.com/rules/axe/3.1/color-contrast?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/color-contrast.js | failureTitle": {
+    "message": "Los colores de fondo y de primer plano no tienen una relación de contraste adecuada."
+  },
+  "lighthouse-core/audits/accessibility/color-contrast.js | title": {
+    "message": "Los colores de fondo y de primer plano tienen una relación de contraste adecuada"
+  },
+  "lighthouse-core/audits/accessibility/definition-list.js | description": {
+    "message": "Si las listas de definiciones no están bien marcadas, es posible que los lectores de pantalla las interpreten de forma confusa o imprecisa. [Más información](https://dequeuniversity.com/rules/axe/3.1/definition-list?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/definition-list.js | failureTitle": {
+    "message": "Los elementos \"<dl>\" no contienen únicamente grupos de \"<dt>\" y \"<dd>\" o elementos \"<script>\" o \"<template>\" ordenados correctamente."
+  },
+  "lighthouse-core/audits/accessibility/definition-list.js | title": {
+    "message": "Los elementos \"<dl>\" solo contienen grupos de \"<dt>\" y \"<dd>\" ordenados correctamente, o elementos \"<script>\" o \"<template>\"."
+  },
+  "lighthouse-core/audits/accessibility/dlitem.js | description": {
+    "message": "Los elementos de la lista de definiciones (\"<dt>\" y \"<dd>\") deben estar incluidos en un elemento \"<dl>\" principal para asegurarte de que los lectores de pantalla puedan leerlos en voz alta correctamente. [Más información](https://dequeuniversity.com/rules/axe/3.1/dlitem?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/dlitem.js | failureTitle": {
+    "message": "Los elementos de la lista de definiciones no están incluidos en elementos \"<dl>\""
+  },
+  "lighthouse-core/audits/accessibility/dlitem.js | title": {
+    "message": "Los elementos de la lista de definiciones se incluyen en los elementos \"<dl>\""
+  },
+  "lighthouse-core/audits/accessibility/document-title.js | description": {
+    "message": "Con el título, los usuarios de lectores de pantalla obtienen una descripción general de la página. Los usuarios de motores de búsqueda lo utilizan para determinar si una página es relevante para su búsqueda. [Más información](https://developers.google.com/web/tools/lighthouse/audits/title)"
+  },
+  "lighthouse-core/audits/accessibility/document-title.js | failureTitle": {
+    "message": "El documento no tiene ningún elemento \"<title>\""
+  },
+  "lighthouse-core/audits/accessibility/document-title.js | title": {
+    "message": "El documento tiene un elemento \"<title>\""
+  },
+  "lighthouse-core/audits/accessibility/duplicate-id.js | description": {
+    "message": "El valor de los atributos id debe ser único para evitar que las tecnologías de asistencia omitan otras instancias. [Más información](https://dequeuniversity.com/rules/axe/3.1/duplicate-id?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/duplicate-id.js | failureTitle": {
+    "message": "Los atributos \"[id]\" de la página no son únicos"
+  },
+  "lighthouse-core/audits/accessibility/duplicate-id.js | title": {
+    "message": "Los atributos \"[id]\" de la página son únicos"
+  },
+  "lighthouse-core/audits/accessibility/frame-title.js | description": {
+    "message": "Los usuarios de lectores de pantalla necesitan que los marcos tengan títulos para que se describa su contenido. [Más información](https://dequeuniversity.com/rules/axe/3.1/frame-title?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/frame-title.js | failureTitle": {
+    "message": "Los elementos \"<frame>\" o \"<iframe>\" no tienen título"
+  },
+  "lighthouse-core/audits/accessibility/frame-title.js | title": {
+    "message": "Los elementos \"<frame>\" o \"<iframe>\" tienen título"
+  },
+  "lighthouse-core/audits/accessibility/html-has-lang.js | description": {
+    "message": "Si no se especifica ningún atributo de idioma para una página, los lectores de pantalla asumirán que la página tiene el idioma predeterminado que el usuario eligió al configurar el lector de pantalla. Si el idioma de la página es diferente del predeterminado, es posible que el lector de pantalla no lea bien el texto de la página. [Más información](https://dequeuniversity.com/rules/axe/3.1/html-has-lang?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/html-has-lang.js | failureTitle": {
+    "message": "El elemento \"<html>\" no contiene ningún atributo \"[lang]\""
+  },
+  "lighthouse-core/audits/accessibility/html-has-lang.js | title": {
+    "message": "El elemento \"<html>\" tiene un atributo \"[lang]\""
+  },
+  "lighthouse-core/audits/accessibility/html-lang-valid.js | description": {
+    "message": "Especificar un [idioma BCP 47] valido (https://www.w3.org/International/questions/qa-choosing-language-tags#question) permite a los lectores de pantalla leer el texto en voz alta correctamente. [Más información](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/html-lang-valid.js | failureTitle": {
+    "message": "El elemento \"<html>\" no tiene un valor válido para el atributo \"[lang]\"."
+  },
+  "lighthouse-core/audits/accessibility/html-lang-valid.js | title": {
+    "message": "El elemento \"<html>\" tiene un valor válido para el atributo \"[lang]\""
+  },
+  "lighthouse-core/audits/accessibility/image-alt.js | description": {
+    "message": "El texto de los elementos informativos debe ser corto y descriptivo. Los elementos decorativos se pueden omitir usando un atributo alt vacío. [Más información](https://dequeuniversity.com/rules/axe/3.1/image-alt?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/image-alt.js | failureTitle": {
+    "message": "Los elementos de imagen no tienen ningún atributo \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/image-alt.js | title": {
+    "message": "Los elementos de imagen tienen atributos \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/input-image-alt.js | description": {
+    "message": "Cuando se usa una imagen como botón \"<input>\", resulta útil proporcionar un texto alternativo para permitir que los usuarios de lectores de pantalla entiendan cuál es la función del botón. [Más información](https://dequeuniversity.com/rules/axe/3.1/input-image-alt?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/input-image-alt.js | failureTitle": {
+    "message": "Los elementos \"<input type=\"image\">\" no tienen texto \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/input-image-alt.js | title": {
+    "message": "Los elementos \"<input type=\"image\">\" no contienen texto \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/label.js | description": {
+    "message": "Las etiquetas facilitan que las tecnologías de asistencia, como los lectores de pantalla, puedan leer los controles de los formularios de forma correcta. [Más información](https://dequeuniversity.com/rules/axe/3.1/label?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/label.js | failureTitle": {
+    "message": "Los elementos de formulario no tienen ninguna etiqueta asociada"
+  },
+  "lighthouse-core/audits/accessibility/label.js | title": {
+    "message": "Los elementos de formulario tienen etiquetas asociadas"
+  },
+  "lighthouse-core/audits/accessibility/layout-table.js | description": {
+    "message": "Una tabla que se utilice para diseño no debe incluir elementos de datos (como ordinales), leyendas ni atributos de resumen, dado que pueden confundir a los usuarios de lectores de pantalla. [Más información](https://dequeuniversity.com/rules/axe/3.1/layout-table?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/layout-table.js | failureTitle": {
+    "message": "Los elementos \"<table>\" de presentación no evitan el uso de \"<th>\", \"<caption>\" ni el atributo \"[summary]\"."
+  },
+  "lighthouse-core/audits/accessibility/layout-table.js | title": {
+    "message": "Los elementos \"<table>\" de presentación no utilizan elementos \"<th>\" o \"<caption>\" ni el atributo \"[summary]\"."
+  },
+  "lighthouse-core/audits/accessibility/link-name.js | description": {
+    "message": "Usar textos de vínculo (y textos alternativos para las imágenes, si estas se usan como vínculos) que sean reconocibles, únicos y que se puedan seleccionar mejora la experiencia de navegación de los usuarios de lectores de pantalla. [Más información](https://dequeuniversity.com/rules/axe/3.1/link-name?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/link-name.js | failureTitle": {
+    "message": "Los vínculos no tienen nombres reconocibles"
+  },
+  "lighthouse-core/audits/accessibility/link-name.js | title": {
+    "message": "Los vínculos tienen nombres reconocibles"
+  },
+  "lighthouse-core/audits/accessibility/list.js | description": {
+    "message": "Los lectores de pantalla leen las listas en voz alta de una forma concreta. Se recomienda utilizar una estructura de listas adecuada para que los lectores de pantalla puedan leer las listas de forma correcta. [Más información](https://dequeuniversity.com/rules/axe/3.1/list?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/list.js | failureTitle": {
+    "message": "Las listas no contienen únicamente elementos \"<li>\" y elementos que admiten scripts (\"<script>\" y \"<template>\")."
+  },
+  "lighthouse-core/audits/accessibility/list.js | title": {
+    "message": "Las listas solo contienen elementos \"<li>\" y elementos que admiten scripts (\"<script>\" y \"<template>\")."
+  },
+  "lighthouse-core/audits/accessibility/listitem.js | description": {
+    "message": "Los lectores de pantalla requieren que los elementos de lista (\"<li>\") estén incluidos dentro de un elemento \"<ul>\" o \"<ol>\" principal para poder leerlos en voz alta correctamente. [Más información](https://dequeuniversity.com/rules/axe/3.1/listitem?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/listitem.js | failureTitle": {
+    "message": "Los elementos de lista (\"<li>\") no se encuentran dentro de los elementos principales \"<ul>\" o \"<ol>\"."
+  },
+  "lighthouse-core/audits/accessibility/listitem.js | title": {
+    "message": "Los objetos de lista (\"<li>\") están incluidos dentro de los elementos principales \"<ul>\" o \"<ol>\""
+  },
+  "lighthouse-core/audits/accessibility/meta-refresh.js | description": {
+    "message": "Los usuarios no esperan que una página se actualice automáticamente. Al hacerlo, se moverá el enfoque a la parte superior de la página. Esta acción puede generar una experiencia frustrante o confusa. [Más información](https://dequeuniversity.com/rules/axe/3.1/meta-refresh?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/meta-refresh.js | failureTitle": {
+    "message": "El documento utiliza \"<meta http-equiv=\"refresh\">\""
+  },
+  "lighthouse-core/audits/accessibility/meta-refresh.js | title": {
+    "message": "El documento no utiliza \"<meta http-equiv=\"refresh\">\""
+  },
+  "lighthouse-core/audits/accessibility/meta-viewport.js | description": {
+    "message": "Desactivar el zoom provoca problemas para los usuarios con visión reducida, ya que necesitan ampliar la pantalla para poder ver correctamente el contenido de las páginas web. [Más información](https://dequeuniversity.com/rules/axe/3.1/meta-viewport?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/meta-viewport.js | failureTitle": {
+    "message": "\"[user-scalable=\"no\"]\" se utiliza en el elemento \"<meta name=\"viewport\">\" o el atributo \"[maximum-scale]\" tiene un valor inferior a 5."
+  },
+  "lighthouse-core/audits/accessibility/meta-viewport.js | title": {
+    "message": "\"[user-scalable=\"no\"]\" no se utiliza en el elemento \"<meta name=\"viewport\">\" y el atributo \"[maximum-scale]\" no tiene un valor inferior a 5."
+  },
+  "lighthouse-core/audits/accessibility/object-alt.js | description": {
+    "message": "Los lectores de pantalla no pueden traducir contenido que no sea texto. Al añadir texto alternativo a los elementos \"<object>\", los lectores de pantalla podrán transmitir su significado a los usuarios. [Más información](https://dequeuniversity.com/rules/axe/3.1/object-alt?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/object-alt.js | failureTitle": {
+    "message": "Los elementos \"<object>\" no tienen texto \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/object-alt.js | title": {
+    "message": "Los elementos \"<object>\" tienen texto \"[alt]\""
+  },
+  "lighthouse-core/audits/accessibility/tabindex.js | description": {
+    "message": "Si el valor es superior a 0, significa que el orden de navegación es explícito. Aunque técnicamente es válido, esto suele producir experiencias frustrantes para los usuarios que necesitan usar tecnologías de asistencia. [Más información](https://dequeuniversity.com/rules/axe/3.1/tabindex?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/tabindex.js | failureTitle": {
+    "message": "Algunos elementos tienen un valor de \"[tabindex]\" superior a 0"
+  },
+  "lighthouse-core/audits/accessibility/tabindex.js | title": {
+    "message": "No hay ningún elemento con un valor de \"[tabindex]\" superior a 0"
+  },
+  "lighthouse-core/audits/accessibility/td-headers-attr.js | description": {
+    "message": "Los lectores de pantalla incluyen funciones para facilitar la navegación por las tablas. Debes asegurarte de que las celdas \"<td>\" que usan el atributo \"[headers]\" solo hacen referencia a otras celdas de la misma tabla, a fin de mejorar la experiencia de los usuarios de lectores de pantalla. [Más información](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/td-headers-attr.js | failureTitle": {
+    "message": "Las celdas de los elementos \"<table>\" que usan el atributo \"[headers]\" hacen referencia a otras celdas de esa misma tabla."
+  },
+  "lighthouse-core/audits/accessibility/td-headers-attr.js | title": {
+    "message": "Las celdas de los elementos \"<table>\" que usan el atributo \"[headers]\" solo hacen referencia a otras celdas de esa misma tabla."
+  },
+  "lighthouse-core/audits/accessibility/th-has-data-cells.js | description": {
+    "message": "Los lectores de pantalla incluyen funciones para facilitar la navegación por las tablas. Asegurarse de que los encabezados de las tablas siempre hagan referencia a un conjunto específico de celdas puede mejora la experiencia de los usuarios de lectores de pantalla. [Más información](https://dequeuniversity.com/rules/axe/3.1/th-has-data-cells?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/th-has-data-cells.js | failureTitle": {
+    "message": "Los elementos \"<th>\" y los elementos con \"[role=\"columnheader\"/\"rowheader\"]\" no contienen las celdas de datos que describen."
+  },
+  "lighthouse-core/audits/accessibility/th-has-data-cells.js | title": {
+    "message": "Los elementos \"<th>\" y los elementos con \"[role=\"columnheader\"/\"rowheader\"]\" contienen las celdas de datos que describen."
+  },
+  "lighthouse-core/audits/accessibility/valid-lang.js | description": {
+    "message": "Especificar un [idioma BCP 47] válido (https://www.w3.org/International/questions/qa-choosing-language-tags#question) en los elementos ayuda a asegurar que los lectores de pantalla pronuncien correctamente las palabras del texto. [Más información](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/valid-lang.js | failureTitle": {
+    "message": "Los atributos \"[lang]\" no tienen un valor válido"
+  },
+  "lighthouse-core/audits/accessibility/valid-lang.js | title": {
+    "message": "Los atributos \"[lang]\" tienen un valor válido"
+  },
+  "lighthouse-core/audits/accessibility/video-caption.js | description": {
+    "message": "Si un video tiene subtítulos, los usuarios sordos o con problemas auditivos pueden acceder a la información con más facilidad. [Más información](https://dequeuniversity.com/rules/axe/3.1/video-caption?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/video-caption.js | failureTitle": {
+    "message": "Los elementos \"<video>\" no contienen ningún elemento \"<track>\" con \"[kind=\"captions\"]\"."
+  },
+  "lighthouse-core/audits/accessibility/video-caption.js | title": {
+    "message": "Los elementos \"<video>\" contienen un elemento \"<track>\" con \"[kind=\"captions\"]\""
+  },
+  "lighthouse-core/audits/accessibility/video-description.js | description": {
+    "message": "Las descripciones de audio proporcionan información relevante en videos cuyos diálogos no transmiten todo el contenido, como en el caso de las expresiones faciales y las escenas. [Más información](https://dequeuniversity.com/rules/axe/3.1/video-description?application=lighthouse)"
+  },
+  "lighthouse-core/audits/accessibility/video-description.js | failureTitle": {
+    "message": "Los elementos \"<video>\" no contienen ningún elemento \"<track>\" con \"[kind=\"description\"]\"."
+  },
+  "lighthouse-core/audits/accessibility/video-description.js | title": {
+    "message": "Los elementos \"<video>\" contienen un elemento \"<track>\" con \"[kind=\"description\"]\""
+  },
+  "lighthouse-core/audits/bootup-time.js | chromeExtensionsWarning": {
+    "message": "Las extensiones de Chrome afectaron de forma negativa al rendimiento de carga de esta página. Prueba a auditarla en modo incógnito o desde un perfil de Chrome sin extensiones."
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptEval": {
+    "message": "Evaluación de la secuencia de comandos"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptParse": {
+    "message": "Análisis de la secuencia de comandos"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnTotal": {
+    "message": "Tiempo de CPU total"
+  },
+  "lighthouse-core/audits/bootup-time.js | description": {
+    "message": "Te recomendamos que reduzcas el tiempo de análisis, compilación y ejecución de JS. Para ello, puedes utilizar cargas útiles de JS más pequeñas. [Más información](https://developers.google.com/web/tools/lighthouse/audits/bootup)"
+  },
+  "lighthouse-core/audits/bootup-time.js | failureTitle": {
+    "message": "Reduce el tiempo de ejecución de JavaScript"
+  },
+  "lighthouse-core/audits/bootup-time.js | title": {
+    "message": "Tiempo de ejecución de JavaScript"
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | description": {
+    "message": "Los GIF de gran tamaño no son eficaces para mostrar contenido animado. En su lugar, considera utilizar formatos de video MPEG4/WebM para animaciones y formatos PNG/WebP para imágenes estáticas a fin de ahorrar bytes de la red. [Más información](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)"
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | title": {
+    "message": "Usa formatos de video para incluir contenido animado"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | description": {
+    "message": "Te recomendamos que uses la carga diferida con imágenes ocultas y que no aparecen en pantalla una vez que todos los recursos críticos hayan terminado de cargarse para reducir el tiempo hasta que la página es interactiva. [Más información](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images)"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | title": {
+    "message": "Posterga la carga de imágenes que no aparecen en pantalla"
+  },
+  "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": {
+    "message": "Hay recursos que bloquean el primer renderizado de la página. Te recomendamos que muestres los elementos JS/CSS críticos insertados y postergues todos los JS/styles que no sean críticos. [Más información](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources)"
+  },
+  "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": {
+    "message": "Elimina los recursos que bloqueen el renderizado"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | description": {
+    "message": "Si la carga útil de la red es muy grande, los usuarios consumen más datos móviles y las páginas tardan más en cargarse. [Más información](https://developers.google.com/web/tools/lighthouse/audits/network-payloads)"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | displayValue": {
+    "message": "Tamaño total: {totalBytes, number, bytes} KB"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | failureTitle": {
+    "message": "Evita cargas útiles de red de gran tamaño"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title": {
+    "message": "Evita cargas útiles de red de gran tamaño"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | description": {
+    "message": "Si reduces los archivos CSS, se puede reducir el tamaño de la carga útil de la red. [Más información](https://developers.google.com/web/tools/lighthouse/audits/minify-css)"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | title": {
+    "message": "Reduce el uso de CSS"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description": {
+    "message": "Si reduces los archivos JavaScript, se puede reducir el tamaño de la carga útil y el tiempo de análisis de la secuencia de comandos. [Más información](https://developers.google.com/speed/docs/insights/MinifyResources)"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | title": {
+    "message": "Reducir el uso de JavaScript"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description": {
+    "message": "Elimina las reglas inactivas de las hojas de estilo y retrasa la carga de las CSS que no se utilicen para el contenido de la mitad superior de la página. Así, se reducirán los bytes consumidos innecesariamente por la actividad de red. [Más información](https://developers.google.com/web/tools/lighthouse/audits/unused-css)"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title": {
+    "message": "Quita los recursos CSS que no se usen"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | description": {
+    "message": "Quita el contenido JavaScript sin usar para reducir el número de bytes que consume la actividad de red."
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | title": {
+    "message": "Quita el código JavaScript sin usar"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | description": {
+    "message": "Una duración en caché más larga puede aumentar el número de visitas repetidas a tu página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/cache-policy)"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | displayValue": {
+    "message": "{itemCount,plural, =1{Se encontró 1 recurso}other{Se encontraron # recursos}}"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle": {
+    "message": "Publica elementos estáticos con una política de caché eficaz"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | title": {
+    "message": "Usa una política de caché eficaz en recursos estáticos"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | description": {
+    "message": "Las imágenes optimizadas se cargan más rápido y consumen menos datos móviles. [Más información](https://developers.google.com/web/tools/lighthouse/audits/optimize-images)"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": {
+    "message": "Codifica las imágenes de forma eficaz"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": {
+    "message": "Muestra imágenes con un tamaño adecuado para ahorrar datos móviles y mejorar el tiempo de carga. [Más información](https://developers.google.com/web/tools/lighthouse/audits/oversized-images)"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | title": {
+    "message": "Usa un tamaño adecuado para las imágenes"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | description": {
+    "message": "Los recursos basados en texto se deberían publicar comprimidos (gzip, deflate o brotli) para minimizar el total de bytes de la red. [Más información](https://developers.google.com/web/tools/lighthouse/audits/text-compression)"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | title": {
+    "message": "Habilita la compresión de texto"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | description": {
+    "message": "Los formatos como JPEG 2000, JPEG XR y WebP comprimen mejor las imágenes que los formatos PNG o JPEG, lo que hace que se descarguen más rápido y consuman menos datos. [Más información](https://developers.google.com/web/tools/lighthouse/audits/webp)"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title": {
+    "message": "Publica imágenes con formatos de próxima generación"
+  },
+  "lighthouse-core/audits/critical-request-chains.js | description": {
+    "message": "Las cadenas de solicitud crítica que se muestran a continuación indican qué recursos son de alta prioridad. Te recomendamos que reduzcas la longitud de las cadenas, disminuyas el tamaño de los recursos o postergues la descarga de recursos innecesarios para mejorar la carga de la página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains)"
+  },
+  "lighthouse-core/audits/critical-request-chains.js | displayValue": {
+    "message": "{itemCount,plural, =1{Se encontró 1 cadena}other{Se encontraron # cadenas}}"
+  },
+  "lighthouse-core/audits/critical-request-chains.js | title": {
+    "message": "Minimiza la profundidad de las solicitudes críticas"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnElement": {
+    "message": "Elemento"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic": {
+    "message": "Estadística"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnValue": {
+    "message": "Valor"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | description": {
+    "message": "Los desarrolladores de navegadores recomiendan que las páginas contengan menos de 1500 elementos de DOM. Lo ideal es conseguir una profundidad de árbol inferior a 32 elementos y a 60 elementos secundarios por cada elemento principal. Los DOM de gran tamaño aumentan el uso de la memoria, hacen que los [cálculos de estilo](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations) duren más y generan costosos [reinicios del flujo del diseño](https://developers.google.com/speed/articles/reflow). [Más información](https://developers.google.com/web/tools/lighthouse/audits/dom-size)"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
+    "message": "{itemCount,plural, =1{1 elemento}other{# elementos}}"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle": {
+    "message": "Evita un tamaño excesivo de DOM"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMDepth": {
+    "message": "Profundidad máxima de DOM"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMElements": {
+    "message": "Total de elementos DOM"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMWidth": {
+    "message": "Número máximo de elementos secundarios"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | title": {
+    "message": "Evita un tamaño excesivo de DOM"
+  },
+  "lighthouse-core/audits/font-display.js | description": {
+    "message": "Utiliza la función de CSS font-display para que los usuarios vean el texto mientras se carga la fuente web. [Más información](https://developers.google.com/web/updates/2016/02/font-display)"
+  },
+  "lighthouse-core/audits/font-display.js | failureTitle": {
+    "message": "Asegúrate de que el texto permanece visible mientras se carga la fuente web"
+  },
+  "lighthouse-core/audits/font-display.js | title": {
+    "message": "Todo el texto permanece visible mientras se carga la fuente web"
+  },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | description": {
+    "message": "Si las páginas se cargan rápidamente en las redes móviles, el usuario disfrutará de una buena experiencia. [Más información](https://developers.google.com/web/tools/lighthouse/audits/fast-3g)"
+  },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueText": {
+    "message": "Interactiva a los {timeInMs, number, seconds} s"
+  },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueTextWithOverride": {
+    "message": "Interactiva en una red móvil simulada a los {timeInMs, number, seconds} s"
+  },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
+    "message": "La página no se carga suficientemente rápido en las redes móviles"
+  },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | title": {
+    "message": "La carga de la página es lo suficientemente rápida en redes móviles"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": {
+    "message": "Categoría"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | description": {
+    "message": "Te recomendamos que reduzcas el tiempo de análisis, compilación y ejecución de JS. Para ello, puedes utilizar cargas útiles de JS más pequeñas."
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | failureTitle": {
+    "message": "Minimiza el trabajo del hilo principal"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | title": {
+    "message": "Minimiza el trabajo del hilo principal"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
+    "message": "La latencia de entrada estimada es el tiempo, en milisegundos, que tarda tu app en responder a las acciones de los usuarios durante el periodo de 5 s más activo de carga de la página. Si la latencia es superior a 50 ms, es posible que los usuarios piensen que tu app es lenta. [Más información](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | title": {
+    "message": "Latencia de entrada estimada"
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | description": {
+    "message": "El Primer procesamiento de imagen con contenido indica el momento en el que se procesa el primer texto o imagen. [Más información](https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint)"
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | title": {
+    "message": "Primer procesamiento de imagen con contenido"
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | description": {
+    "message": "El primer tiempo inactivo de la CPU indica la primera vez que el hilo principal de la página está lo suficientemente inactivo para recibir acciones del usuario. [Más información](https://developers.google.com/web/tools/lighthouse/audits/first-interactive)"
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | title": {
+    "message": "Primer tiempo inactivo de la CPU"
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | description": {
+    "message": "La primera puntura significativa mide el momento en que se muestra el contenido principal de la página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint)"
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | title": {
+    "message": "Primera pintura significativa"
+  },
+  "lighthouse-core/audits/metrics/interactive.js | description": {
+    "message": "El tiempo de carga es el tiempo que tarda una página en ser totalmente interactiva. [Más información](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive)"
+  },
+  "lighthouse-core/audits/metrics/interactive.js | title": {
+    "message": "Tiempo de carga"
+  },
+  "lighthouse-core/audits/metrics/max-potential-fid.js | description": {
+    "message": "El posible retraso de primera entrada que podrían experimentar los usuarios es la duración (en milisegundos) de la tarea más larga."
+  },
+  "lighthouse-core/audits/metrics/max-potential-fid.js | title": {
+    "message": "FID potencial máxima"
+  },
+  "lighthouse-core/audits/metrics/speed-index.js | description": {
+    "message": "El índice de velocidad indica la rapidez con la que se puede ver el contenido de una página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/speed-index)"
+  },
+  "lighthouse-core/audits/metrics/speed-index.js | title": {
+    "message": "Índice de velocidad"
+  },
+  "lighthouse-core/audits/network-rtt.js | description": {
+    "message": "Los tiempos de ida y vuelta (RTT) de la red afectan mucho al rendimiento. Un RTT alto hasta un origen indica que usar servidores más cercanos al usuario podría mejorar el rendimiento. [Más información](https://hpbn.co/primer-on-latency-and-bandwidth/)"
+  },
+  "lighthouse-core/audits/network-rtt.js | title": {
+    "message": "Tiempos de ida y vuelta de la red"
+  },
+  "lighthouse-core/audits/network-server-latency.js | description": {
+    "message": "Las latencias del servidor pueden afectar el rendimiento de la Web. Una latencia del servidor alta en un origen indica que el servidor está sobrecargado o que su rendimiento de backend es bajo. [Más información](https://hpbn.co/primer-on-web-performance/#analyzing-the-resource-waterfall)"
+  },
+  "lighthouse-core/audits/network-server-latency.js | title": {
+    "message": "Latencias de backend del servidor"
+  },
+  "lighthouse-core/audits/redirects.js | description": {
+    "message": "Las redirecciones provocan retrasos adicionales antes de que la página se pueda cargar. [Más información](https://developers.google.com/web/tools/lighthouse/audits/redirects)"
+  },
+  "lighthouse-core/audits/redirects.js | title": {
+    "message": "Evita que haya varias redirecciones de página"
+  },
+  "lighthouse-core/audits/seo/canonical.js | description": {
+    "message": "Los vínculos canónicos sugieren qué URL mostrar en los resultados de la búsqueda. [Más información](https://developers.google.com/web/tools/lighthouse/audits/canonical)"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationConflict": {
+    "message": "Múltiples URL en conflicto ({urlList})"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationDifferentDomain": {
+    "message": "Apunta a un dominio diferente ({url})"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationInvalid": {
+    "message": "URL no válida ({url})"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationPointsElsewhere": {
+    "message": "Apunta a otra ubicación \"hreflang\" ({url})"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationRelative": {
+    "message": "URL relativa ({url})"
+  },
+  "lighthouse-core/audits/seo/canonical.js | explanationRoot": {
+    "message": "Apunta a la URL raíz del dominio (la página principal), en lugar de a una página de contenido equivalente"
+  },
+  "lighthouse-core/audits/seo/canonical.js | failureTitle": {
+    "message": "El documento no tiene un vínculo \"rel=canonical\" válido"
+  },
+  "lighthouse-core/audits/seo/canonical.js | title": {
+    "message": "El documento tiene un enlace \"rel=canonical\" válido"
+  },
+  "lighthouse-core/audits/seo/font-size.js | description": {
+    "message": "Las fuentes con un tamaño inferior a 12 px son demasiado pequeñas y poco legibles, lo que obliga a los visitantes que acceden con dispositivos móviles a pellizcar la pantalla para ampliarla y poder leer el texto. Intenta que más del 60% del texto de la página tenga un tamaño igual o superior a 12 px. [Más información](https://developers.google.com/web/tools/lighthouse/audits/font-sizes)"
+  },
+  "lighthouse-core/audits/seo/font-size.js | displayValue": {
+    "message": "{decimalProportion, number, extendedPercent} del texto es legible"
+  },
+  "lighthouse-core/audits/seo/font-size.js | explanation": {
+    "message": "El {decimalProportion, number, extendedPercent} del texto es demasiado pequeño."
+  },
+  "lighthouse-core/audits/seo/font-size.js | explanationViewport": {
+    "message": "El texto es ilegible porque no hay metaetiquetas de de la vista del puerto optimizadas para pantallas de dispositivos móviles."
+  },
+  "lighthouse-core/audits/seo/font-size.js | explanationWithDisclaimer": {
+    "message": "El {decimalProportion, number, extendedPercent} del texto es demasiado pequeño (según la muestra {decimalProportionVisited, number, extendedPercent})."
+  },
+  "lighthouse-core/audits/seo/font-size.js | failureTitle": {
+    "message": "El documento no usa tamaños de fuente legibles"
+  },
+  "lighthouse-core/audits/seo/font-size.js | title": {
+    "message": "El documento usa tamaños de fuente legibles"
+  },
+  "lighthouse-core/audits/seo/hreflang.js | description": {
+    "message": "Los vínculos de hreflang indican a los motores de búsqueda qué versiones de las páginas deben incluir en los resultados de búsqueda de una región o un idioma determinados. [Más información](https://developers.google.com/web/tools/lighthouse/audits/hreflang)"
+  },
+  "lighthouse-core/audits/seo/hreflang.js | failureTitle": {
+    "message": "El atributo \"hreflang\" del documento no es válido"
+  },
+  "lighthouse-core/audits/seo/hreflang.js | title": {
+    "message": "El atributo \"hreflang\" del documento es válido"
+  },
+  "lighthouse-core/audits/seo/http-status-code.js | description": {
+    "message": "Es posible que las páginas con códigos de estado HTTP incorrectos no estén bien indexadas. [Más información](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code)"
+  },
+  "lighthouse-core/audits/seo/http-status-code.js | failureTitle": {
+    "message": "El código de estado HTTP de la página no es válido"
+  },
+  "lighthouse-core/audits/seo/http-status-code.js | title": {
+    "message": "El código de estado HTTP de la página es válido"
+  },
+  "lighthouse-core/audits/seo/is-crawlable.js | description": {
+    "message": "Los motores de búsqueda no pueden incluir tus páginas en los resultados de búsqueda si no tienen permiso para rastrearlas. [Más información](https://developers.google.com/web/tools/lighthouse/audits/indexing)"
+  },
+  "lighthouse-core/audits/seo/is-crawlable.js | failureTitle": {
+    "message": "Se bloqueó la indexación de la página"
+  },
+  "lighthouse-core/audits/seo/is-crawlable.js | title": {
+    "message": "No se bloqueó la indexación de la página"
+  },
+  "lighthouse-core/audits/seo/link-text.js | description": {
+    "message": "El texto descriptivo de los vínculos ayuda a los motores de búsqueda a entender tu contenido. [Más información](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text)"
+  },
+  "lighthouse-core/audits/seo/link-text.js | displayValue": {
+    "message": "{itemCount,plural, =1{Se encontró 1 vínculo}other{Se encontraron # vínculos}}"
+  },
+  "lighthouse-core/audits/seo/link-text.js | failureTitle": {
+    "message": "Los vínculos no tienen texto descriptivo"
+  },
+  "lighthouse-core/audits/seo/link-text.js | title": {
+    "message": "Los vínculos tienen texto descriptivo"
+  },
+  "lighthouse-core/audits/seo/manual/structured-data.js | description": {
+    "message": "Ejecuta la [Herramienta de pruebas de datos estructurados](https://search.google.com/structured-data/testing-tool/) y la herramienta [Structured Data Linter](http://linter.structured-data.org/) para validar los datos estructurados. [Más información](https://developers.google.com/search/docs/guides/mark-up-content)"
+  },
+  "lighthouse-core/audits/seo/manual/structured-data.js | title": {
+    "message": "Los datos estructurados son válidos"
+  },
+  "lighthouse-core/audits/seo/meta-description.js | description": {
+    "message": "Se pueden incluir metadescripciones en los resultados de búsqueda para resumir brevemente el contenido de la página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/description)"
+  },
+  "lighthouse-core/audits/seo/meta-description.js | explanation": {
+    "message": "El texto de la descripción está vacío."
+  },
+  "lighthouse-core/audits/seo/meta-description.js | failureTitle": {
+    "message": "El documento no tiene una metadescripción"
+  },
+  "lighthouse-core/audits/seo/meta-description.js | title": {
+    "message": "El documento tiene una metadescripción"
+  },
+  "lighthouse-core/audits/seo/plugins.js | description": {
+    "message": "Los motores de búsqueda no pueden indexar el contenido de los complementos, y muchos dispositivos limitan el uso de complementos o no los admiten. [Más información](https://developers.google.com/web/tools/lighthouse/audits/plugins)"
+  },
+  "lighthouse-core/audits/seo/plugins.js | failureTitle": {
+    "message": "El documento usa complementos"
+  },
+  "lighthouse-core/audits/seo/plugins.js | title": {
+    "message": "Los documentos evitan el uso de complementos"
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | description": {
+    "message": "Si el formato del archivo robots.txt no es correcto, es posible que los rastreadores no puedan interpretar cómo quieres que se rastree o indexe tu sitio web."
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | displayValueHttpBadCode": {
+    "message": "la solicitud de robots.txt ha devuelto el siguiente estado de HTTP: {statusCode}"
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | displayValueValidationError": {
+    "message": "{itemCount,plural, =1{Se encontró 1 error}other{Se encontraron # errores}}"
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | explanation": {
+    "message": "Lighthouse no pudo descargar un archivo robots.txt"
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | failureTitle": {
+    "message": "robots.txt no es válido"
+  },
+  "lighthouse-core/audits/seo/robots-txt.js | title": {
+    "message": "robots.txt es válido"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | description": {
+    "message": "Los elementos interactivos, como los botones y enlaces, deben ser suficientemente grandes (48 × 48 px) y tener suficiente espacio alrededor para poder tocarlos con facilidad sin superponerse con otros elementos. [Más información](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design)"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | displayValue": {
+    "message": "El {decimalProportion, number, percent} de los elementos táctiles tiene un tamaño adecuado"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | explanationViewportMetaNotOptimized": {
+    "message": "Los elementos táctiles son demasiado pequeños porque no hay metaetiquetas de viewport optimizadas para pantallas de dispositivos móviles"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | failureTitle": {
+    "message": "El tamaño de los elementos táctiles no es el adecuado"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | overlappingTargetHeader": {
+    "message": "Elementos superpuestos"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | sizeHeader": {
+    "message": "Tamaño"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | tapTargetHeader": {
+    "message": "Elemento táctil"
+  },
+  "lighthouse-core/audits/seo/tap-targets.js | title": {
+    "message": "El tamaño de los elementos táctiles es el adecuado"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | description": {
+    "message": "El tiempo hasta el primer byte indica el momento en el que el servidor envía una respuesta. [Más información](https://developers.google.com/web/tools/lighthouse/audits/ttfb)"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | displayValue": {
+    "message": "El documento raíz ha tardado {timeInMs, number, milliseconds} ms"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | failureTitle": {
+    "message": "Reduce los tiempos de respuesta del servidor (TTFB)"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | title": {
+    "message": "La respuesta del servidor es lenta (TTFB)"
+  },
+  "lighthouse-core/audits/user-timings.js | columnDuration": {
+    "message": "Duración"
+  },
+  "lighthouse-core/audits/user-timings.js | columnName": {
+    "message": "Nombre"
+  },
+  "lighthouse-core/audits/user-timings.js | columnStartTime": {
+    "message": "Hora de inicio"
+  },
+  "lighthouse-core/audits/user-timings.js | columnType": {
+    "message": "Tipo"
+  },
+  "lighthouse-core/audits/user-timings.js | description": {
+    "message": "Te recomendamos que uses la API de Tiempos de usuario en tu app para calcular su rendimiento real durante las principales experiencias de usuario. [Más información](https://developers.google.com/web/tools/lighthouse/audits/user-timing)"
+  },
+  "lighthouse-core/audits/user-timings.js | displayValue": {
+    "message": "{itemCount,plural, =1{1 tiempo de usuario}other{# tiempos de usuario}}"
+  },
+  "lighthouse-core/audits/user-timings.js | title": {
+    "message": "Medidas y marcas de Tiempos de usuario"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | crossoriginWarning": {
+    "message": "Se encontró un elemento <link> previo a la conexión para \"{securityOrigin}\", pero el navegador no lo usó. Comprueba que el atributo \"crossorigin\" se esté usando correctamente."
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | description": {
+    "message": "Te recomendamos que agregues sugerencias de recursos preconnect o dns-prefetch para establecer conexiones previas con orígenes externos importantes. [Más información](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect)"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | title": {
+    "message": "Establece conexión previamente con los orígenes necesarios"
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | crossoriginWarning": {
+    "message": "Se encontró un elemento <link> de precarga para \"{preloadURL}\", pero el navegador no lo utilizó. Comprueba que el atributo \"crossorigin\" se esté usando correctamente."
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | description": {
+    "message": "Te recomendamos que uses <link rel=preload> para dar prioridad a los recursos que se solicitan más tarde al cargar la página. [Más información](https://developers.google.com/web/tools/lighthouse/audits/preload)"
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | title": {
+    "message": "Carga previamente las solicitudes clave"
+  },
+  "lighthouse-core/config/default-config.js | a11yAriaGroupDescription": {
+    "message": "A continuación se indican consejos para optimizar el uso de ARIA en tu app, lo que puede mejorar la experiencia de los usuarios de tecnologías de asistencia, como los lectores de pantalla."
+  },
+  "lighthouse-core/config/default-config.js | a11yAriaGroupTitle": {
+    "message": "ARIA"
+  },
+  "lighthouse-core/config/default-config.js | a11yAudioVideoGroupDescription": {
+    "message": "Aquí tienes consejos para proporcionar contenido alternativo para audio y video. Así se puede mejorar la experiencia de los usuarios con dificultades auditivas o visuales."
+  },
+  "lighthouse-core/config/default-config.js | a11yAudioVideoGroupTitle": {
+    "message": "Audio y video"
+  },
+  "lighthouse-core/config/default-config.js | a11yBestPracticesGroupDescription": {
+    "message": "Estos elementos destacan las prácticas recomendadas de accesibilidad más habituales."
+  },
+  "lighthouse-core/config/default-config.js | a11yBestPracticesGroupTitle": {
+    "message": "Prácticas recomendadas"
+  },
+  "lighthouse-core/config/default-config.js | a11yCategoryDescription": {
+    "message": "Estas comprobaciones incluyen consejos para [mejorar la accesibilidad de tu app web](https://developers.google.com/web/fundamentals/accessibility). Solo se pueden detectar un subconjunto de problemas de accesibilidad de forma automática. Por eso, te recomendamos realizar pruebas manuales."
+  },
+  "lighthouse-core/config/default-config.js | a11yCategoryManualDescription": {
+    "message": "Estos elementos se ocupan de áreas que las herramientas de prueba automáticas no pueden analizar. Obtén más información en nuestra guía sobre [conducting an accessibility review](https://developers.google.com/web/fundamentals/accessibility/how-to-review)."
+  },
+  "lighthouse-core/config/default-config.js | a11yCategoryTitle": {
+    "message": "Accesibilidad"
+  },
+  "lighthouse-core/config/default-config.js | a11yColorContrastGroupDescription": {
+    "message": "A continuación se indican consejos para facilitar la lectura del contenido."
+  },
+  "lighthouse-core/config/default-config.js | a11yColorContrastGroupTitle": {
+    "message": "Contraste"
+  },
+  "lighthouse-core/config/default-config.js | a11yLanguageGroupDescription": {
+    "message": "A continuación se indican consejos para que los usuarios con diversas configuraciones regionales puedan interpretar mejor el contenido de las páginas."
+  },
+  "lighthouse-core/config/default-config.js | a11yLanguageGroupTitle": {
+    "message": "Internacionalización y localización"
+  },
+  "lighthouse-core/config/default-config.js | a11yNamesLabelsGroupDescription": {
+    "message": "A continuación se indican consejos para mejorar la semántica de los controles de tu app. Estos consejos pueden mejorar la experiencia de los usuarios de tecnologías de asistencia, como los lectores de pantalla."
+  },
+  "lighthouse-core/config/default-config.js | a11yNamesLabelsGroupTitle": {
+    "message": "Nombres y etiquetas"
+  },
+  "lighthouse-core/config/default-config.js | a11yNavigationGroupDescription": {
+    "message": "Estas son oportunidades para mejorar la navegación con el teclado en tu app."
+  },
+  "lighthouse-core/config/default-config.js | a11yNavigationGroupTitle": {
+    "message": "Navegación"
+  },
+  "lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupDescription": {
+    "message": "Aquí tienes consejos para mejorar la lectura de datos en tablas o listas con tecnologías de asistencia como los lectores de pantalla."
+  },
+  "lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupTitle": {
+    "message": "Tablas y listas"
+  },
+  "lighthouse-core/config/default-config.js | diagnosticsGroupDescription": {
+    "message": "Obtén más información sobre el rendimiento de tu app."
+  },
+  "lighthouse-core/config/default-config.js | diagnosticsGroupTitle": {
+    "message": "Diagnóstico"
+  },
+  "lighthouse-core/config/default-config.js | firstPaintImprovementsGroupDescription": {
+    "message": "El aspecto más importante del rendimiento es la rapidez con la que se renderizan los píxeles en la pantalla. Métricas clave: Primer procesamiento de imagen con contenido, Primera pintura significativa"
+  },
+  "lighthouse-core/config/default-config.js | firstPaintImprovementsGroupTitle": {
+    "message": "Mejoras del primer procesamiento de imagen"
+  },
+  "lighthouse-core/config/default-config.js | loadOpportunitiesGroupDescription": {
+    "message": "Estas optimizaciones pueden hacer que tus páginas se carguen más rápido."
+  },
+  "lighthouse-core/config/default-config.js | loadOpportunitiesGroupTitle": {
+    "message": "Oportunidades"
+  },
+  "lighthouse-core/config/default-config.js | metricGroupTitle": {
+    "message": "Métricas"
+  },
+  "lighthouse-core/config/default-config.js | overallImprovementsGroupDescription": {
+    "message": "Mejora la experiencia de carga general para que la página responda bien y se pueda usar lo antes posible. Métricas clave: Tiempo de carga, Índice de velocidad"
+  },
+  "lighthouse-core/config/default-config.js | overallImprovementsGroupTitle": {
+    "message": "Mejoras generales"
+  },
+  "lighthouse-core/config/default-config.js | performanceCategoryTitle": {
+    "message": "Rendimiento"
+  },
+  "lighthouse-core/config/default-config.js | pwaFastReliableGroupTitle": {
+    "message": "Rápida y confiable"
+  },
+  "lighthouse-core/config/default-config.js | pwaInstallableGroupTitle": {
+    "message": "Instalable"
+  },
+  "lighthouse-core/config/default-config.js | pwaOptimizedGroupTitle": {
+    "message": "Optimizado para PWA"
+  },
+  "lighthouse-core/config/default-config.js | seoCategoryDescription": {
+    "message": "Estas comprobaciones aseguran que tu página esté optimizada para posicionarse en los resultados de los motores de búsqueda. Hay otros factores que Lighthouse no comprueba y que pueden afectar a tu posicionamiento en los buscadores. [Más información](https://support.google.com/webmasters/answer/35769)"
+  },
+  "lighthouse-core/config/default-config.js | seoCategoryManualDescription": {
+    "message": "Ejecuta estos validadores adicionales en tu sitio web para comprobar más prácticas recomendadas de SEO."
+  },
+  "lighthouse-core/config/default-config.js | seoCategoryTitle": {
+    "message": "SEO"
+  },
+  "lighthouse-core/config/default-config.js | seoContentGroupDescription": {
+    "message": "Edita el código HTML de tu página web de forma que los rastreadores puedan entender mejor el contenido de tu app."
+  },
+  "lighthouse-core/config/default-config.js | seoContentGroupTitle": {
+    "message": "Prácticas recomendadas para el contenido"
+  },
+  "lighthouse-core/config/default-config.js | seoCrawlingGroupDescription": {
+    "message": "Para aparecer en los resultados de búsqueda, los rastreadores necesitan acceso a tu app."
+  },
+  "lighthouse-core/config/default-config.js | seoCrawlingGroupTitle": {
+    "message": "Rastreo e indexación"
+  },
+  "lighthouse-core/config/default-config.js | seoMobileGroupDescription": {
+    "message": "Asegúrate de que tus páginas estén optimizadas para dispositivos móviles para que los usuarios no tengan que pellizcar ni hacer zoom para leer las páginas de contenido. [Más información](https://developers.google.com/search/mobile-sites/)"
+  },
+  "lighthouse-core/config/default-config.js | seoMobileGroupTitle": {
+    "message": "Optimizada para dispositivos móviles"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": {
+    "message": "TTL en caché"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnSize": {
+    "message": "Tamaño (KB)"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnTimeSpent": {
+    "message": "Tiempo de uso"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnURL": {
+    "message": "URL"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnWastedBytes": {
+    "message": "Ahorro posible (KB)"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | columnWastedMs": {
+    "message": "Ahorro posible (ms)"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | displayValueByteSavings": {
+    "message": "Ahorro posible de {wastedBytes, number, bytes} KB"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | displayValueMsSavings": {
+    "message": "Ahorro posible de {wastedMs, number, milliseconds} ms"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | ms": {
+    "message": "{timeInMs, number, milliseconds} ms"
+  },
+  "lighthouse-core/lib/i18n/i18n.js | seconds": {
+    "message": "{timeInMs, number, seconds} s"
+  },
+  "lighthouse-core/lib/lh-error.js | badTraceRecording": {
+    "message": "Se produjo un error al registrar el rastro durante la carga de la página. Vuelve a ejecutar Lighthouse. ({errorCode})"
+  },
+  "lighthouse-core/lib/lh-error.js | criTimeout": {
+    "message": "Se agotó el tiempo de espera de la conexión inicial del protocolo del depurador."
+  },
+  "lighthouse-core/lib/lh-error.js | didntCollectScreenshots": {
+    "message": "Chrome no recopiló ninguna captura de pantalla al cargar la página. Comprueba que haya contenido visible en la página y vuelve a ejecutar Lighthouse. ({errorCode})"
+  },
+  "lighthouse-core/lib/lh-error.js | dnsFailure": {
+    "message": "Los servidores DNS no pudieron resolver el dominio proporcionado."
+  },
+  "lighthouse-core/lib/lh-error.js | internalChromeError": {
+    "message": "Se produjo un error interno de Chrome. Reinicia Chrome y vuelve a ejecutar Lighthouse."
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailed": {
+    "message": "Lighthouse no pudo cargar correctamente la página que solicitaste. Comprueba que estás probando la URL correcta y que el servidor responde correctamente a todas las solicitudes."
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailedHung": {
+    "message": "Lighthouse no pudo cargar correctamente la URL que solicitaste porque la página dejó de responder."
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailedInsecure": {
+    "message": "Las credenciales de seguridad de la URL que proporcionaste no son válidas. {securityMessages}"
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailedWithDetails": {
+    "message": "Lighthouse no pudo cargar correctamente la página que solicitaste. Comprueba que estás probando la URL correcta y que el servidor responde correctamente a todas las solicitudes. (Detalles: {errorDetails})"
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadFailedWithStatusCode": {
+    "message": "Lighthouse no pudo cargar correctamente la página que solicitaste. Comprueba que estás probando la URL correcta y que el servidor responde correctamente a todas las solicitudes. (Código de estado: {statusCode})"
+  },
+  "lighthouse-core/lib/lh-error.js | pageLoadTookTooLong": {
+    "message": "La página tardó demasiado en cargarse. Sigue los consejos del informe para reducir el tiempo de carga de la página y vuelve a ejecutar Lighthouse. ({errorCode})"
+  },
+  "lighthouse-core/lib/lh-error.js | protocolTimeout": {
+    "message": "Se superó el tiempo asignado para la respuesta de protocolo de DevTools. (Método: {protocolMethod})"
+  },
+  "lighthouse-core/lib/lh-error.js | requestContentTimeout": {
+    "message": "Se superó el tiempo asignado para obtener el contenido de los recursos"
+  },
+  "lighthouse-core/lib/lh-error.js | urlInvalid": {
+    "message": "Al parecer, la URL que proporcionaste no es válida."
+  },
+  "lighthouse-core/report/html/renderer/util.js | auditGroupExpandTooltip": {
+    "message": "Mostrar auditorías"
+  },
+  "lighthouse-core/report/html/renderer/util.js | crcInitialNavigation": {
+    "message": "Navegación inicial"
+  },
+  "lighthouse-core/report/html/renderer/util.js | crcLongestDurationLabel": {
+    "message": "Latencia de ruta crítica máxima:"
+  },
+  "lighthouse-core/report/html/renderer/util.js | errorLabel": {
+    "message": "Error"
+  },
+  "lighthouse-core/report/html/renderer/util.js | errorMissingAuditInfo": {
+    "message": "Error del informe: No hay información de la auditoría"
+  },
+  "lighthouse-core/report/html/renderer/util.js | labDataTitle": {
+    "message": "Datos de prueba"
+  },
+  "lighthouse-core/report/html/renderer/util.js | lsPerformanceCategoryDescription": {
+    "message": "[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analizó la página actual en una red móvil emulada. Los valores son estimados y pueden variar."
+  },
+  "lighthouse-core/report/html/renderer/util.js | manualAuditsGroupTitle": {
+    "message": "Elementos adicionales que se deben comprobar manualmente"
+  },
+  "lighthouse-core/report/html/renderer/util.js | notApplicableAuditsGroupTitle": {
+    "message": "No aplicable"
+  },
+  "lighthouse-core/report/html/renderer/util.js | opportunityResourceColumnLabel": {
+    "message": "Oportunidad"
+  },
+  "lighthouse-core/report/html/renderer/util.js | opportunitySavingsColumnLabel": {
+    "message": "Ahorro estimado"
+  },
+  "lighthouse-core/report/html/renderer/util.js | passedAuditsGroupTitle": {
+    "message": "Auditorías aprobadas"
+  },
+  "lighthouse-core/report/html/renderer/util.js | snippetCollapseButtonLabel": {
+    "message": "Contraer fragmento"
+  },
+  "lighthouse-core/report/html/renderer/util.js | snippetExpandButtonLabel": {
+    "message": "Expandir fragmento"
+  },
+  "lighthouse-core/report/html/renderer/util.js | toplevelWarningsMessage": {
+    "message": "Algunos problemas afectaron la ejecución de Lighthouse:"
+  },
+  "lighthouse-core/report/html/renderer/util.js | varianceDisclaimer": {
+    "message": "Los valores son estimados y pueden variar."
+  },
+  "lighthouse-core/report/html/renderer/util.js | warningAuditsGroupTitle": {
+    "message": "Auditorías aprobadas con advertencias"
+  },
+  "lighthouse-core/report/html/renderer/util.js | warningHeader": {
+    "message": "Advertencias: "
+  },
+  "stack-packs/packs/wordpress.js | efficient_animated_content": {
+    "message": "Puedes subir tu GIF a un servicio que permita insertarlo como un video HTML5."
+  },
+  "stack-packs/packs/wordpress.js | offscreen_images": {
+    "message": "Instala un [complemento de carga diferida de WordPress](https://wordpress.org/plugins/search/lazy+load/) con la capacidad de postergar imágenes que no aparecen en la pantalla, o cambia a un tema con esa función. También puedes usar [el complemento AMP](https://wordpress.org/plugins/amp/)."
+  },
+  "stack-packs/packs/wordpress.js | render_blocking_resources": {
+    "message": "Existen varios complementos de WordPress que pueden ayudarte a [insertar recursos fundamentales](https://wordpress.org/plugins/search/critical+css/) o a [postergar precursos menos importantes](https://wordpress.org/plugins/search/defer+css+javascript/). Ten en cuenta que las optimizaciones que ofrecen estos complementos pueden bloquear funciones de tu tema o tus complementos, así que seguramente tengas que hacer cambios en el código."
+  },
+  "stack-packs/packs/wordpress.js | time_to_first_byte": {
+    "message": "Los temas, los complementos y las especificaciones del servidor afectan al tiempo de respuesta. Puedes buscar un tema más optimizado, seleccionar un complemento de optimización o actualizar tu servidor."
+  },
+  "stack-packs/packs/wordpress.js | total_byte_weight": {
+    "message": "Puedes mostrar fragmentos en tus listas de entradas (por ejemplo, mediante la etiqueta \"<more>\"), reducir la cantidad de entradas que se muestran en cada página, dividir tus entradas más largas en múltiples páginas o usar un complemento para postergar la carga de los comentarios."
+  },
+  "stack-packs/packs/wordpress.js | unminified_css": {
+    "message": "Hay varios [complementos de WordPress](https://wordpress.org/plugins/search/minify+css/) que pueden concatenar, reducir y comprimir tus estilos para acelerar tu sitio web. Te recomendamos que, si es posible, uses un proceso de creación para realizar la reducción de forma anticipada."
+  },
+  "stack-packs/packs/wordpress.js | unminified_javascript": {
+    "message": "Hay varios [complementos de WordPress](https://wordpress.org/plugins/search/minify+javascript/) que pueden concatenar, reducir y comprimir tus estilos para acelerar tu sitio web. Te recomendamos que, si es posible, uses un proceso de creación para realizar la reducción de forma anticipada."
+  },
+  "stack-packs/packs/wordpress.js | unused_css_rules": {
+    "message": "Puedes reducir o cambiar la cantidad de [complementos de WordPress](https://wordpress.org/plugins/) que cargan archivos CSS sin usar en tu página. Para identificar los complementos que agregan archivos CSS externos, ejecuta [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) en DevTools de Chrome. Puedes identificar el tema o complemento en la URL de la hoja de estilo. Presta atención a los complementos con varias hojas de estilo en la lista y con muchos elementos en rojo en la cobertura de código. Un complemento solo debería poner en cola una hoja de estilo (si esta se usa en la página)."
+  },
+  "stack-packs/packs/wordpress.js | unused_javascript": {
+    "message": "Puedes reducir o cambiar la cantidad de [complementos de WordPress](https://wordpress.org/plugins/) que cargan JavaScript sin usar en tu página. Para identificar los complementos que agregan JavaScript externo, ejecuta [cobertura de código](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) en DevTools de Chrome. Puedes identificar el tema o complemento concreto en la URL de la secuencia de comandos. Presta atención a los complementos con varias secuencias de comandos en la lista y con muchos elementos en rojo en la cobertura de código. Un complemento solo debería poner en cola una secuencia de comandos (si esta se usa en la página)."
+  },
+  "stack-packs/packs/wordpress.js | uses_long_cache_ttl": {
+    "message": "Consulta información sobre el [almacenamiento en la memoria caché del navegador en WordPress](https://codex.wordpress.org/WordPress_Optimization#Browser_Caching)."
+  },
+  "stack-packs/packs/wordpress.js | uses_optimized_images": {
+    "message": "Puedes utilizar un [complemento de optimización de imágenes de WordPress](https://wordpress.org/plugins/search/optimize+images/) que comprima tus imágenes conservando la calidad."
+  },
+  "stack-packs/packs/wordpress.js | uses_responsive_images": {
+    "message": "Carga imágenes directamente a través de [media library](https://codex.wordpress.org/Media_Library_Screen) para garantizar que estén disponibles los tamaños de imagen requeridos y, luego, insértalas desde la biblioteca multimedia o usa el widget de imágenes para asegurarte de que se utilicen los tamaños de imagen óptimos (incluidos los que se utilizan para interrupciones de respuestas). Evita usar imágenes \"completas\", a menos que las dimensiones sean adecuadas para su uso. [Más información](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size)"
+  },
+  "stack-packs/packs/wordpress.js | uses_text_compression": {
+    "message": "Puedes habilitar la compresión de texto en la configuración de tu servidor web."
+  },
+  "stack-packs/packs/wordpress.js | uses_webp_images": {
+    "message": "Puedes utilizar un [complemento](https://wordpress.org/plugins/search/convert+webp/) o un servicio que convierta automáticamente las imágenes que subas en los formatos óptimos."
+  }
+}
\ No newline at end of file
diff --git a/lighthouse-core/lib/i18n/locales/pt.json b/lighthouse-core/lib/i18n/locales/pt.json
index 53e806e90..6b47079c9 100644
--- a/lighthouse-core/lib/i18n/locales/pt.json
+++ b/lighthouse-core/lib/i18n/locales/pt.json
@@ -534,7 +534,7 @@
     "message": "Tempo até ficar interativa"
   },
   "lighthouse-core/audits/metrics/max-potential-fid.js | description": {
-    "message": "O primeiro atraso de entrada em potencial que seus usuários notariam seria a duração, em milésimos de segundo, da tarefa mais longa."
+    "message": "A potencial latência de entrada que seus usuários notariam seria a duração, em milésimos de segundo, da tarefa mais longa."
   },
   "lighthouse-core/audits/metrics/max-potential-fid.js | title": {
     "message": "Potencial máx. de 1º atraso de entrada"
diff --git a/lighthouse-core/lib/lh-error.js b/lighthouse-core/lib/lh-error.js
index fdbf06d7a..cf2c5be0d 100644
--- a/lighthouse-core/lib/lh-error.js
+++ b/lighthouse-core/lib/lh-error.js
@@ -23,6 +23,8 @@ const UIStrings = {
   pageLoadFailedWithDetails: 'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: {errorDetails})',
   /** Error message explaining that the security certificate of the page Lighthouse observed was invalid, so the URL cannot be accessed. securityMessages will be replaced with one or more strings from the browser explaining what was insecure about the page load. */
   pageLoadFailedInsecure: 'The URL you have provided does not have a valid security certificate. {securityMessages}',
+  /** Error message explaining that Chrome prevented the page from loading and displayed an interstitial screen instead, so the URL cannot be accessed. */
+  pageLoadFailedInterstitial: 'Chrome prevented page load with an interstitial. Make sure you are testing the correct URL and that the server is properly responding to all requests.',
   /** Error message explaining that Chrome has encountered an error during the Lighthouse run, and that Chrome should be restarted. */
   internalChromeError: 'An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.',
   /** Error message explaining that fetching the resources of the webpage has taken longer than the maximum time. */
@@ -180,6 +182,13 @@ const ERRORS = {
     message: UIStrings.pageLoadFailedInsecure,
     lhrRuntimeError: true,
   },
+  /* Used when any Chrome interstitial error prevents page load.
+   */
+  CHROME_INTERSTITIAL_ERROR: {
+    code: 'CHROME_INTERSTITIAL_ERROR',
+    message: UIStrings.pageLoadFailedInterstitial,
+    lhrRuntimeError: true,
+  },
   /* Used when the page stopped responding and did not finish loading. */
   PAGE_HUNG: {
     code: 'PAGE_HUNG',
diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js
index 58083b5ee..1193bb3a8 100644
--- a/lighthouse-core/report/html/renderer/performance-category-renderer.js
+++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js
@@ -123,14 +123,14 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
     }
 
     // Metrics.
-    const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
     const metricAuditsEl = this.renderAuditGroup(groups.metrics);
 
     // Metric descriptions toggle.
     const toggleTmpl = this.dom.cloneTemplate('#tmpl-lh-metrics-toggle', this.templateContext);
     const toggleEl = this.dom.find('.lh-metrics-toggle', toggleTmpl);
-    metricAuditsEl.prepend(...toggleEl.childNodes);
+    metricAuditsEl.append(...toggleEl.childNodes);
 
+    const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
     const keyMetrics = metricAudits.filter(a => a.weight >= 3);
     const otherMetrics = metricAudits.filter(a => a.weight < 3);
 
diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css
index d1752a9dc..4377ad093 100644
--- a/lighthouse-core/report/html/report-styles.css
+++ b/lighthouse-core/report/html/report-styles.css
@@ -15,52 +15,68 @@
  * limitations under the License.
  */
 
+/*
+  Naming convention:
+
+  If a variable is used for a specific component: --{component}-{property name}-{modifier}
+
+  Both {component} and {property name} should be kebab-case. If the target is the entire page,
+  use 'report' for the component. The property name should be not be abbreviated. Use the
+  property name the variable is intended for - if it's used for multiple, a common descriptor
+  is fine (ex: 'size' for a variable applied to 'width' and 'height'). If a variable is shared
+  across multiple components, either create more variables or just drop the "{component}-"
+  part of the name. Append any modifiers at the end (ex: 'big', 'dark').
+
+  For colors: --color-{hue}-{intensity}
+
+  {intensity} is the Material Design tag - 700, A700, etc. Omit "-{intensity}" if there is only one
+  color of that hue.
+*/
 .lh-vars {
   --text-font-family: Roboto, Helvetica, Arial, sans-serif;
   --monospace-font-family: 'Roboto Mono', 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;
-  --body-background-color: #fff;
-  --body-text-color: var(--color-black-900);
-  --body-font-size: 16px;
-  --body-line-height: 24px;
-  --subheader-color: hsl(206, 6%, 25%);
-  --header-font-size: 20px;
+  --report-background-color: #fff;
+  --report-text-color: var(--color-gray-900);
+  --report-font-size: 16px;
+  --report-line-height: 24px;
+  --snippet-color: var(--color-gray-800);
+  --category-header-font-size: 20px;
   --header-line-height: 24px;
-  --caption-line-height: 16px;
+  --audit-explanation-line-height: 16px;
   --default-padding: 12px;
-  --section-padding: 16px;
-  --section-indent: 16px;
-  --audit-item-gap: 5px;
-  --text-indent: 8px;
-  --secondary-text-color: var(--color-black-800);
+  --footer-padding-vertical: 16px;
+  --audit-margin-horizontal: 5px;
+  --stackpack-padding-horizontal: 10px;
+  --secondary-text-color: var(--color-gray-800);
   --informative-color: var(--color-blue-900);
-  --medium-75-gray: #757575;
-  --medium-50-gray: hsl(210, 17%, 98%);
+  --snippet-background-color: var(--color-gray-50);
   --report-secondary-border-color: #ebebeb;
-  --display-value-gray: hsl(216, 5%, 39%);
-  --report-width: calc(60 * var(--body-font-size));
+  --chevron-line-stroke: var(--color-gray-600);
+  --report-width: calc(60 * var(--report-font-size));
   --report-min-width: 400px;
-  --lh-score-icon-background-size: 24px;
-  --lh-tools-icon-size: var(--lh-score-icon-background-size);
-  --lh-tools-icon-color: var(--medium-75-gray);
-  --lh-table-higlight-bg: hsla(0, 0%, 75%, 0.1);
-  --lh-sparkline-height: 5px;
-  --lh-audit-vpadding: 8px;
-  --lh-audit-group-vpadding: 8px;
-  --lh-section-vpadding: 12px;
+  --score-icon-background-size: 24px;
+  --tools-icon-size: var(--score-icon-background-size);
+  --tools-icon-color: var(--color-gray-600);
+  --table-higlight-background-color: hsla(0, 0%, 75%, 0.1);
+  --sparkline-height: 5px;
+  --audit-padding-vertical: 8px;
+  --audit-group-padding-vertical: 8px;
+  --section-padding-vertical: 12px;
   --chevron-size: 12px;
-  --inner-audit-left-padding: calc(var(--score-shape-size) + var(--score-shape-margin-left) + var(--score-shape-margin-right));
+  --audit-description-padding-left: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right));
 
   /* Palette using Material Design Colors
    * https://www.materialui.co/colors */
-  --color-black-100: #F5F5F5;
-  --color-black-200: #E0E0E0;
-  --color-black-400: #BDBDBD;
-  --color-black-500: #9E9E9E;
-  --color-black-600: #757575;
-  --color-black-800: #424242;
-  --color-black-900: #212121;
-  --color-black: #000000;
-  --color-blue: #2962FF;
+  --color-gray-50: #FAFAFA;
+  --color-gray-100: #F5F5F5;
+  --color-gray-200: #E0E0E0;
+  --color-gray-400: #BDBDBD;
+  --color-gray-500: #9E9E9E;
+  --color-gray-600: #757575;
+  --color-gray-800: #424242;
+  --color-gray-900: #212121;
+  --color-gray: #000000;
+  --color-blue-A700: #2962FF;
   --color-green-700: #018642;
   --color-green: #0CCE6B;
   --color-orange-700: #D04900;
@@ -73,43 +89,43 @@
   --color-cyan-500: #00BCD4;
   --color-teal-600: #00897B;
 
-
-  /* TODO(cjamcl) clean up unused variables. */
-  --audits-margin-bottom: 40px;
-  --env-item-bg: var(--color-black-100);
+  --audit-group-margin-bottom: 40px;
+  --env-item-background-color: var(--color-gray-100);
   --env-name-min-width: 220px;
-  --env-tem-padding: 10px 0px;
-  --expandable-padding: 0 0 2px calc(var(--score-shape-margin-left) + var(--score-shape-size) + var(--score-shape-margin-right));
+  --env-item-padding: 10px 0px;
+  --env-item-font-size: 28px;
+  --env-item-line-height: 36px;
+  --metric-description-padding: 0 0 2px calc(var(--score-icon-margin-left) + var(--score-icon-size) + var(--score-icon-margin-right));
   --gauge-circle-size-big: 112px;
   --gauge-circle-size: 80px;
-  --header-padding: 20px 0 20px 0;
-  --icon-square-size: calc(var(--score-shape-size) * 0.88);
-  --plugin-badge-bg: var(--color-white);
+  --scores-container-padding: 20px 0 20px 0;
+  --icon-square-size: calc(var(--score-icon-size) * 0.88);
+  --plugin-badge-background-color: var(--color-white);
   --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);
   --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);
   --plugin-icon-size: 65%;
   --pwa-icon-margin: 0 6px 0 -2px;
-  --pwa-icon-size: var(--topbar-icon-size);
+  --pwa-icon-size: var(--topbar-logo-size);
   --score-container-padding: 8px;
-  --score-container-width: 148px;
-  --score-number-font-size-big: 38px;
-  --score-number-font-size: 28px;
-  --score-shape-margin-left: 4px;
-  --score-shape-margin-right: 12px;
-  --score-shape-margin: 0 var(--score-shape-margin-right) 0 var(--score-shape-margin-left);
-  --score-shape-size: 12px;
-  --score-title-font-size-big: 28px;
-  --score-title-font-size: 20px;
-  --score-title-line-height-big: 36px;
-  --score-title-line-height: 26px;
+  --gauge-wrapper-width: 148px;
+  --gauge-percentage-font-size-big: 38px;
+  --gauge-percentage-font-size: 28px;
+  --score-icon-margin-left: 4px;
+  --score-icon-margin-right: 12px;
+  --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);
+  --score-icon-size: 12px;
+  --gauge-label-font-size-big: 28px;
+  --gauge-label-font-size: 20px;
+  --gauge-label-line-height-big: 36px;
+  --gauge-label-line-height: 26px;
   --scorescale-height: 6px;
   --scorescale-width: 18px;
-  --section-padding: 40px;
-  --topbar-bg: var(--color-black-100);
+  --category-padding: 40px;
+  --topbar-background-color: var(--color-gray-100);
   --topbar-height: 32px;
-  --topbar-icon-size: 24px;
+  --topbar-logo-size: 24px;
   --topbar-padding: 0 8px;
-  --metrics-toggle-color: var(--color-black-200);
+  --metrics-toggle-background-color: var(--color-gray-200);
 
   --color-average-secondary: var(--color-orange-700);
   --color-average: var(--color-orange);
@@ -117,11 +133,11 @@
   --color-fail: var(--color-red);
   --color-pass-secondary: var(--color-green-700);
   --color-pass: var(--color-green);
-  --color-sticky-header-bg: var(--body-background-color);
-  --color-highlighter-bg: var(--body-text-color);
+  --sticky-header-background-color: var(--report-background-color);
+  --highlighter-background-color: var(--report-text-color);
 
   --color-hover: #FAFAFA;
-  --color-metric-toggle-lines: #7F7F7F;
+  --metric-toggle-lines-fill: #7F7F7F;
 
   --plugin-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="%23757575"><path d="M0 0h24v24H0z" fill="none"/><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></svg>');
   --plugin-icon-url-dark: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="%23FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></svg>');
@@ -149,29 +165,28 @@
     --color-green-700: var(--color-green);
     --color-teal-600: var(--color-cyan-500);
     --color-orange-700: var(--color-orange);
-  
-    --color-black-200: var(--color-black-800);
-    --color-black-400: var(--color-black-600);
-    --color-black-600: var(--color-black-500);
-  
-    --topbar-bg: var(--color-black);
-    --plugin-badge-bg: var(--color-black-800);
-    --env-item-bg: var(--color-black);
-    --report-secondary-border-color: var(--color-black-200);
-  
-    --body-background-color: var(--color-black-900);
-    --body-text-color: var(--color-black-100);
-    --secondary-text-color: var(--color-black-400);
-  
+
+    --color-gray-200: var(--color-gray-800);
+    --color-gray-400: var(--color-gray-600);
+    --color-gray-600: var(--color-gray-500);
+
+    --topbar-background-color: var(--color-gray);
+    --plugin-badge-background-color: var(--color-gray-800);
+    --env-item-background-color: var(--color-gray);
+    --report-secondary-border-color: var(--color-gray-200);
+
+    --report-background-color: var(--color-gray-900);
+    --report-text-color: var(--color-gray-100);
+    --secondary-text-color: var(--color-gray-400);
+
     --plugin-icon-url: var(--plugin-icon-url-dark);
-  
+
     --informative-color: var(--color-blue-200);
-  
-    --medium-50-gray: #757575;
-    --medium-75-gray: var(--color-white);
-  
+
+    --color-gray-50: #757575;
+
     --color-hover: rgba(0, 0, 0, 0.2);
-  
+
     --pwa-fast-reliable-gray-url: var(--pwa-fast-reliable-gray-url-dark);
     --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark);
     --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark);
@@ -180,27 +195,27 @@
 
 @media only screen and (max-width: 480px) {
   .lh-vars {
-    --audits-margin-bottom: 20px;
-    --body-font-size: 14px;
-    --body-line-height: 20px;
+    --audit-group-margin-bottom: 20px;
+    --report-font-size: 14px;
+    --report-line-height: 20px;
     --env-name-min-width: 120px;
     --gauge-circle-size-big: 96px;
     --gauge-circle-size: 72px;
     --header-padding: 16px 0 16px 0;
     --plugin-icon-size: 75%;
     --pwa-icon-margin: 0 7px 0 -3px;
-    --score-container-width: 112px;
-    --score-number-font-size-big: 34px;
-    --score-number-font-size: 26px;
-    --score-shape-margin-left: 2px;
-    --score-shape-size: 10px;
-    --score-title-font-size-big: 22px;
-    --score-title-font-size: 14px;
-    --score-title-line-height-big: 26px;
-    --score-title-line-height: 20px;
-    --section-padding: 24px;
+    --gauge-wrapper-width: 112px;
+    --gauge-percentage-font-size-big: 34px;
+    --gauge-percentage-font-size: 26px;
+    --score-icon-margin-left: 2px;
+    --score-icon-size: 10px;
+    --gauge-label-font-size-big: 22px;
+    --gauge-label-font-size: 14px;
+    --gauge-label-line-height-big: 26px;
+    --gauge-label-line-height: 20px;
+    --category-padding: 24px;
     --topbar-height: 28px;
-    --topbar-icon-size: 20px;
+    --topbar-logo-size: 20px;
   }
 
   /* Not enough space to adequately show the relative savings bars. */
@@ -212,36 +227,36 @@
 .lh-vars.lh-devtools {
   --text-font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif;
   --monospace-font-family: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;
-  --body-font-size: 12px;
-  --body-line-height: 20px;
-  --header-font-size: 16px;
+  --report-font-size: 12px;
+  --report-line-height: 20px;
+  --category-header-font-size: 16px;
   --header-line-height: 20px;
-  --caption-line-height: 14px;
+  --audit-explanation-line-height: 14px;
   --default-padding: 12px;
-  --section-padding: 12px;
-  --section-indent: 8px;
+  --category-padding: 12px;
+  --footer-padding-vertical: 8px;
 
   --gauge-circle-size-big: 72px;
   --gauge-circle-size: 64px;
 
-  --audits-margin-bottom: 20px;
+  --audit-group-margin-bottom: 20px;
   --env-name-min-width: 120px;
   --header-padding: 16px 0 16px 0;
   --plugin-icon-size: 75%;
   --pwa-icon-margin: 0 7px 0 -3px;
-  --score-container-width: 97px;
-  --score-number-font-size-big: 34px;
-  --score-number-font-size: 26px;
-  --score-shape-margin-left: 2px;
-  --score-shape-size: 10px;
-  --score-title-font-size-big: 22px;
-  --score-title-font-size: 14px;
-  --score-title-line-height-big: 26px;
-  --score-title-line-height: 20px;
+  --gauge-wrapper-width: 97px;
+  --gauge-percentage-font-size-big: 34px;
+  --gauge-percentage-font-size: 26px;
+  --score-icon-margin-left: 2px;
+  --score-icon-size: 10px;
+  --gauge-label-font-size-big: 22px;
+  --gauge-label-font-size: 14px;
+  --gauge-label-line-height-big: 26px;
+  --gauge-label-line-height: 20px;
 
-  --lh-audit-vpadding: 4px;
-  --lh-audit-group-vpadding: 12px;
-  --lh-section-vpadding: 8px;
+  --audit-padding-vertical: 4px;
+  --audit-group-padding-vertical: 12px;
+  --section-padding-vertical: 8px;
 }
 
 .lh-devtools.lh-root {
@@ -268,12 +283,12 @@
 
 .lh-root {
   font-family: var(--text-font-family);
-  font-size: var(--body-font-size);
+  font-size: var(--report-font-size);
   margin: 0;
-  line-height: var(--body-line-height);
-  background: var(--body-background-color);
+  line-height: var(--report-line-height);
+  background: var(--report-background-color);
   scroll-behavior: smooth;
-  color: var(--body-text-color);
+  color: var(--report-text-color);
 }
 
 .lh-root :focus {
@@ -321,18 +336,18 @@
 }
 
 .lh-audit__description, .lh-audit__stackpack {
-  --inner-audit-right-padding: calc(var(--text-indent) + 2px);
-  padding-left: var(--inner-audit-left-padding);
-  padding-right: var(--inner-audit-right-padding);
+  --inner-audit-padding-right: var(--stackpack-padding-horizontal);
+  padding-left: var(--audit-description-padding-left);
+  padding-right: var(--inner-audit-padding-right);
   padding-top: 8px;
   padding-bottom: 8px;
 }
 
 .lh-details {
-  font-size: var(--body-font-size);
+  font-size: var(--report-font-size);
   margin-top: var(--default-padding);
   margin-bottom: var(--default-padding);
-  margin-left: var(--inner-audit-left-padding);
+  margin-left: var(--audit-description-padding-left);
   /* whatever the .lh-details side margins are */
   width: 100%;
 }
@@ -391,9 +406,9 @@
 /* Score */
 
 .lh-audit__score-icon {
-  width: var(--score-shape-size);
-  height: var(--score-shape-size);
-  margin: var(--score-shape-margin);
+  width: var(--score-icon-size);
+  height: var(--score-icon-size);
+  margin: var(--score-icon-margin);
 }
 
 .lh-audit--pass .lh-audit__display-text {
@@ -417,19 +432,19 @@
   color: var(--color-fail-secondary);
 }
 .lh-audit--fail .lh-audit__score-icon {
-  border-left: calc(var(--score-shape-size) / 2) solid transparent;
-  border-right: calc(var(--score-shape-size) / 2) solid transparent;
-  border-bottom: var(--score-shape-size) solid var(--color-fail);
+  border-left: calc(var(--score-icon-size) / 2) solid transparent;
+  border-right: calc(var(--score-icon-size) / 2) solid transparent;
+  border-bottom: var(--score-icon-size) solid var(--color-fail);
 }
 
 .lh-audit--manual .lh-audit__display-text,
 .lh-audit--notapplicable .lh-audit__display-text {
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
 }
 .lh-audit--manual .lh-audit__score-icon,
 .lh-audit--notapplicable .lh-audit__score-icon {
   border-radius: 100%;
-  background: var(--color-black-400);
+  background: var(--color-gray-400);
 }
 
 .lh-audit--error .lh-audit__score-icon {
@@ -437,13 +452,13 @@
 }
 
 .lh-audit--informative .lh-audit__display-text {
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
 }
 
 .lh-audit--informative .lh-audit__score-icon {
   border: none;
   border-radius: 100%;
-  background: var(--color-black-400);
+  background: var(--color-gray-400);
 }
 
 .lh-audit__description,
@@ -451,7 +466,7 @@
   color: var(--secondary-text-color);
 }
 .lh-category-header__description  {
-  font-size: var(--body-font-size);
+  font-size: var(--report-font-size);
   text-align: center;
   margin: 0px auto;
   max-width: 400px;
@@ -461,7 +476,7 @@
 .lh-audit__display-text,
 .lh-load-opportunity__sparkline,
 .lh-chevron-container {
-  margin: 0 var(--audit-item-gap);
+  margin: 0 var(--audit-margin-horizontal);
 }
 .lh-chevron-container {
   margin-right: 0;
@@ -474,7 +489,7 @@
 /* Prepend display text with em dash separator. But not in Opportunities. */
 .lh-audit__display-text:not(:empty):before {
   content: '—';
-  margin-right: var(--audit-item-gap);
+  margin-right: var(--audit-margin-horizontal);
 }
 .lh-audit-group.lh-audit-group--load-opportunities .lh-audit__display-text:not(:empty):before {
   display: none;
@@ -485,7 +500,7 @@
   display: flex;
   align-items: center;
   font-weight: 500;
-  padding: var(--lh-audit-vpadding) 0;
+  padding: var(--audit-padding-vertical) 0;
 }
 
 .lh-audit--load-opportunity .lh-audit__header {
@@ -575,14 +590,14 @@
 }
 
 .lh-metrics__disclaimer {
-  color: var(--medium-75-gray);
-  margin: var(--lh-section-vpadding) 0;
+  color: var(--color-gray-600);
+  margin: var(--section-padding-vertical) 0;
 }
 
 .lh-metric__description {
   display: none;
   color: var(--secondary-text-color);
-  padding: var(--expandable-padding);
+  padding: var(--metric-description-padding);
 }
 
 .lh-metric__value {
@@ -628,15 +643,15 @@
 }
 .lh-metrics-toggle__input:not(:checked) + label .lh-metrics-toggle__icon--less,
 .lh-metrics-toggle__input:checked + label .lh-metrics-toggle__icon--more {
-  background-color: var(--color-blue);
-  --color-metric-toggle-lines: var(--color-white);
+  background-color: var(--color-blue-A700);
+  --metric-toggle-lines-fill: var(--color-white);
 }
 .lh-metrics-toggle__lines {
-  fill: var(--color-metric-toggle-lines);
+  fill: var(--metric-toggle-lines-fill);
 }
 
 .lh-metrics-toggle__label  {
-  background-color: var(--metrics-toggle-color);
+  background-color: var(--metrics-toggle-background-color);
 }
 
 .lh-metrics-toggle__label .lh-metrics-toggle__icon--less {
@@ -656,10 +671,10 @@
 
 .lh-metric .lh-metric__innerwrap::before {
   content: '';
-  width: var(--score-shape-size);
-  height: var(--score-shape-size);
+  width: var(--score-icon-size);
+  height: var(--score-icon-size);
   display: inline-block;
-  margin: var(--score-shape-margin);
+  margin: var(--score-icon-margin);
 }
 
 .lh-metric--pass .lh-metric__value {
@@ -683,9 +698,9 @@
   color: var(--color-fail-secondary);
 }
 .lh-metric--fail .lh-metric__innerwrap::before {
-  border-left: calc(var(--score-shape-size) / 2) solid transparent;
-  border-right: calc(var(--score-shape-size) / 2) solid transparent;
-  border-bottom: var(--score-shape-size) solid var(--color-fail);
+  border-left: calc(var(--score-icon-size) / 2) solid transparent;
+  border-right: calc(var(--score-icon-size) / 2) solid transparent;
+  border-bottom: var(--score-icon-size) solid var(--color-fail);
 }
 
 .lh-metric--error .lh-metric__value,
@@ -706,9 +721,9 @@
 }
 
 .lh-load-opportunity__header .lh-load-opportunity__col {
-  color: var(--medium-75-gray);
+  color: var(--color-gray-600);
   display: unset;
-  line-height: calc(2.3 * var(--body-font-size));
+  line-height: calc(2.3 * var(--report-font-size));
 }
 
 .lh-load-opportunity__col {
@@ -727,7 +742,7 @@
 
 .lh-audit--load-opportunity .lh-audit__display-text {
   text-align: right;
-  flex: 0 0 calc(3 * var(--body-font-size));
+  flex: 0 0 calc(3 * var(--report-font-size));
 }
 
 
@@ -735,11 +750,11 @@
 
 .lh-load-opportunity__sparkline {
   flex: 1;
-  margin-top: calc((var(--body-line-height) - var(--lh-sparkline-height)) / 2);
+  margin-top: calc((var(--report-line-height) - var(--sparkline-height)) / 2);
 }
 
 .lh-sparkline {
-  height: var(--lh-sparkline-height);
+  height: var(--sparkline-height);
   width: 100%;
 }
 
@@ -823,7 +838,7 @@
 /* Audit Group */
 
 .lh-audit-group {
-  margin-bottom: var(--audits-margin-bottom);
+  margin-bottom: var(--audit-group-margin-bottom);
   position: relative;
 }
 
@@ -882,22 +897,21 @@
 .lh-audit-group__summary {
   display: flex;
   justify-content: space-between;
-  padding-right: var(--text-indent);
-  margin-top: calc(var(--section-padding) * 1.5);
-  margin-bottom: var(--section-padding);
+  margin-top: calc(var(--category-padding) * 1.5);
+  margin-bottom: var(--category-padding);
 }
 
 .lh-audit-group__itemcount {
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
   font-weight: bold;
 }
 .lh-audit-group__header .lh-chevron {
-  margin-top: calc((var(--body-line-height) - 5px) / 2);
+  margin-top: calc((var(--report-line-height) - 5px) / 2);
 }
 
 .lh-audit-group__header {
-  font-size: var(--body-font-size);
-  margin: 0 0 var(--lh-audit-group-vpadding);
+  font-size: var(--report-font-size);
+  margin: 0 0 var(--audit-group-padding-vertical);
   /* When the header takes 100% width, the chevron becomes small. */
   max-width: calc(100% - var(--chevron-size));
 }
@@ -912,16 +926,16 @@
 
 .lh-audit-group__header span.lh-audit-group__itemcount {
   font-weight: bold;
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
 }
 
 .lh-audit-group__header span.lh-audit-group__description {
   font-weight: 500;
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
 }
 .lh-audit-group__header span.lh-audit-group__description::before {
   content: '—';
-  margin: 0px var(--audit-item-gap);
+  margin: 0px var(--audit-margin-horizontal);
 }
 
 .lh-clump > .lh-audit-group__header,
@@ -931,12 +945,12 @@
 .lh-audit-group--pwa-fast-reliable .lh-audit-group__header,
 .lh-audit-group--pwa-installable .lh-audit-group__header,
 .lh-audit-group--pwa-optimized .lh-audit-group__header {
-  margin-top: var(--lh-audit-group-vpadding);
+  margin-top: var(--audit-group-padding-vertical);
 }
 
 .lh-audit-explanation {
-  margin: var(--lh-audit-vpadding) 0 calc(var(--lh-audit-vpadding) / 2);
-  line-height: var(--caption-line-height);
+  margin: var(--audit-padding-vertical) 0 calc(var(--audit-padding-vertical) / 2);
+  line-height: var(--audit-explanation-line-height);
 }
 
 .lh-audit--fail .lh-audit-explanation {
@@ -970,10 +984,10 @@
 }
 
 .lh-warnings {
-  --item-margin: calc(var(--body-line-height) / 6);
+  --item-margin: calc(var(--report-line-height) / 6);
   color: var(--color-average);
-  margin: var(--lh-audit-vpadding) 0;
-  padding: calc(var(--lh-audit-vpadding) / 2) var(--lh-audit-vpadding);
+  margin: var(--audit-padding-vertical) 0;
+  padding: calc(var(--audit-padding-vertical) / 2) var(--audit-padding-vertical);
 }
 .lh-warnings span {
   font-weight: bold;
@@ -982,11 +996,11 @@
 .lh-warnings--toplevel {
   --item-margin: calc(var(--header-line-height) / 4);
   color: var(--secondary-text-color);
-  margin: var(--section-padding);
-  padding: var(--section-padding);
+  margin: var(--category-padding);
+  padding: var(--category-padding);
 }
 .lh-warnings ul {
-  padding-left: calc(var(--section-padding) * 2);
+  padding-left: calc(var(--category-padding) * 2);
   margin: 0;
 }
 .lh-warnings li {
@@ -1062,7 +1076,7 @@
 .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {
   width: var(--plugin-badge-size);
   height: var(--plugin-badge-size);
-  background-color: var(--plugin-badge-bg);
+  background-color: var(--plugin-badge-background-color);
   background-image: var(--plugin-icon-url);
   background-repeat: no-repeat;
   background-size: var(--plugin-icon-size);
@@ -1098,7 +1112,7 @@
 
 .lh-category .lh-gauge__percentage {
   --gauge-circle-size: var(--gauge-circle-size-big);
-  --score-number-font-size: var(--score-number-font-size-big);
+  --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);
 }
 
 .lh-gauge__wrapper {
@@ -1118,17 +1132,17 @@
 }
 
 .lh-gauge__label {
-  font-size: var(--score-title-font-size);
-  line-height: var(--score-title-line-height);
+  font-size: var(--gauge-label-font-size);
+  line-height: var(--gauge-label-line-height);
   margin-top: 10px;
   text-align: center;
-  color: var(--body-text-color);
+  color: var(--report-text-color);
 }
 
 /* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */
 .lh-category .lh-gauge__label {
-  --score-title-font-size: var(--score-title-font-size-big);
-  --score-title-line-height: var(--score-title-line-height-big);
+  --gauge-label-font-size: var(--gauge-label-font-size-big);
+  --gauge-label-line-height: var(--gauge-label-line-height-big);
   margin-top: 14px;
 }
 
@@ -1137,7 +1151,7 @@
 .lh-scores-header .lh-gauge--pwa__wrapper,
 .lh-sticky-header .lh-gauge__wrapper,
 .lh-sticky-header .lh-gauge--pwa__wrapper {
-  width: var(--score-container-width);
+  width: var(--gauge-wrapper-width);
 }
 
 .lh-scores-header .lh-gauge--pwa__wrapper {
@@ -1148,7 +1162,7 @@
 .lh-scorescale {
   display: inline-flex;
   margin: 12px auto 0 auto;
-  border: 1px solid var(--color-black-200);
+  border: 1px solid var(--color-gray-200);
   border-radius: 20px;
   padding: 8px 8px;
 }
@@ -1194,17 +1208,17 @@
 }
 
 .lh-category {
-  padding: var(--section-padding);
+  padding: var(--category-padding);
   max-width: var(--report-width);
   margin: 0 auto;
 }
 
 .lh-category-wrapper {
-  border-bottom: 1px solid var(--color-black-200);
+  border-bottom: 1px solid var(--color-gray-200);
 }
 
 .lh-category-wrapper:first-of-type {
-  border-top: 1px solid var(--color-black-200);
+  border-top: 1px solid var(--color-gray-200);
 }
 
 /* section hash link jump should preserve fixed header
@@ -1220,9 +1234,9 @@
 }
 
 .lh-category-header {
-  font-size: var(--header-font-size);
+  font-size: var(--category-header-font-size);
   min-height: var(--gauge-circle-size);
-  margin-bottom: var(--lh-section-vpadding);
+  margin-bottom: var(--section-padding-vertical);
 }
 
 .lh-category-header .lh-score__gauge {
@@ -1232,7 +1246,7 @@
 }
 
 .lh-category-header .lh-audit__title {
-  font-size: var(--header-font-size);
+  font-size: var(--category-header-font-size);
   line-height: var(--header-line-height);
 }
 
@@ -1289,18 +1303,18 @@
   --image-preview-size: 24px;
   border-collapse: collapse;
   /* Can't assign padding to table, so shorten the width instead. */
-  width: calc(100% - var(--inner-audit-left-padding));
+  width: calc(100% - var(--audit-description-padding-left));
 }
 
 .lh-table thead th {
   font-weight: normal;
-  color: var(--color-black-600);
+  color: var(--color-gray-600);
   /* See text-wrapping comment on .lh-container. */
   word-break: normal;
 }
 
 .lh-table tbody tr:nth-child(odd) {
-  background-color: var(--lh-table-higlight-bg);
+  background-color: var(--table-higlight-background-color);
 }
 
 .lh-table th,
@@ -1361,7 +1375,7 @@
 }
 
 .lh-text__url-host {
-  margin-left: calc(var(--body-font-size) / 2);
+  margin-left: calc(var(--report-font-size) / 2);
   opacity: 0.6;
   font-size: 90%
 }
@@ -1381,15 +1395,15 @@
   --chevron-angle-right: -42deg;
   width: var(--chevron-size);
   height: var(--chevron-size);
-  margin-top: calc((var(--body-line-height) - 12px) / 2);
+  margin-top: calc((var(--report-line-height) - 12px) / 2);
 }
 
 .lh-chevron__lines {
   transition: transform 0.4s;
-  transform: translateY(var(--body-line-height));
+  transform: translateY(var(--report-line-height));
 }
 .lh-chevron__line {
- stroke: var(--display-value-gray);
+ stroke: var(--chevron-line-stroke);
  stroke-width: var(--chevron-size);
  stroke-linecap: square;
  transform-origin: 50%;
diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html
index 01498480b..b4097829d 100644
--- a/lighthouse-core/report/html/templates.html
+++ b/lighthouse-core/report/html/templates.html
@@ -71,7 +71,7 @@ limitations under the License.
 <!-- Lighthouse metrics toggle -->
 <template id="tmpl-lh-metrics-toggle">
   <div class="lh-metrics-toggle">
-    <input class="lh-metrics-toggle__input" type="checkbox" name="toggle-metric-descriptions">
+    <input class="lh-metrics-toggle__input" type="checkbox" id="toggle-metric-descriptions" aria-label="Toggle the display of metric descriptions">
     <label class="lh-metrics-toggle__label" for="toggle-metric-descriptions">
       <div class="lh-metrics-toggle__icon lh-metrics-toggle__icon--less" aria-hidden="true">
         <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">
@@ -159,7 +159,7 @@ limitations under the License.
     .lh-scores-container {
       display: flex;
       flex-direction: column;
-      padding: var(--header-padding);
+      padding: var(--scores-container-padding);
       position: relative;
       width: 100%;
     }
@@ -168,8 +168,8 @@ limitations under the License.
       --gauge-circle-size: 36px;
       --plugin-badge-size: 18px;
       --plugin-icon-size: 75%;
-      --score-container-width: 60px;
-      --score-number-font-size: 13px;
+      --gauge-wrapper-width: 60px;
+      --gauge-percentage-font-size: 13px;
       position: sticky;
       left: 0;
       right: 0;
@@ -177,8 +177,8 @@ limitations under the License.
       font-weight: 700;
       display: none;
       justify-content: center;
-      background-color: var(--color-sticky-header-bg);
-      border-bottom: 1px solid var(--color-black-200);
+      background-color: var(--sticky-header-background-color);
+      border-bottom: 1px solid var(--color-gray-200);
       padding-top: var(--score-container-padding);
       padding-bottom: 4px;
       z-index: 1;
@@ -202,9 +202,9 @@ limitations under the License.
     }
 
     .lh-highlighter {
-      width: var(--score-container-width);
+      width: var(--gauge-wrapper-width);
       height: 1px;
-      background: var(--color-highlighter-bg);
+      background-color: var(--highlighter-background-color);
       /* Position at bottom of first gauge in sticky header. */
       position: absolute;
       grid-column: 1;
@@ -237,23 +237,23 @@ limitations under the License.
       display: flex;
       align-items: center;
       height: var(--topbar-height);
-      background-color: var(--topbar-bg);
+      background-color: var(--topbar-background-color);
       padding: var(--topbar-padding);
     }
 
     .lh-topbar__logo {
-      width: var(--topbar-icon-size);
-      height: var(--topbar-icon-size);
+      width: var(--topbar-logo-size);
+      height: var(--topbar-logo-size);
       user-select: none;
     }
     .lh-topbar__logo .shape {
-      fill: var(--body-text-color);
+      fill: var(--report-text-color);
     }
 
     .lh-topbar__url {
       margin: var(--topbar-padding);
       text-decoration: none;
-      color: var(--body-text-color);
+      color: var(--report-text-color);
     }
 
     .lh-tools {
@@ -261,8 +261,8 @@ limitations under the License.
       will-change: transform;
     }
     .lh-tools__button {
-      width: var(--lh-tools-icon-size);
-      height: var(--lh-tools-icon-size);
+      width: var(--tools-icon-size);
+      height: var(--tools-icon-size);
       cursor: pointer;
       margin-right: 5px;
       /* This is actually a button element, but we want to style it like a transparent div. */
@@ -275,7 +275,7 @@ limitations under the License.
       outline: inherit;
     }
     .lh-tools__button svg {
-      fill: var(--lh-tools-icon-color);
+      fill: var(--tools-icon-color);
     }
     .dark .lh-tools__button svg {
       filter: invert(1);
@@ -287,7 +287,7 @@ limitations under the License.
     }
     .lh-tools__dropdown {
       position: absolute;
-      background-color: var(--body-background-color);
+      background-color: var(--report-background-color);
       border: 1px solid var(--report-border-color);
       border-radius: 3px;
       padding: calc(var(--default-padding) / 2) 0;
@@ -311,7 +311,7 @@ limitations under the License.
     }
     .lh-tools__dropdown a:hover,
     .lh-tools__dropdown a:focus {
-      background-color: var(--color-black-200);
+      background-color: var(--color-gray-200);
       outline: none;
     }
     .lh-tools__dropdown .report-icon {
@@ -323,7 +323,7 @@ limitations under the License.
       text-indent: 18px;
     }
     .dark .report-icon {
-      color: var(--color-black-900);
+      color: var(--color-gray-900);
       filter: invert(1);
     }
     .dark .lh-tools__dropdown a:hover,
@@ -366,7 +366,7 @@ limitations under the License.
     <a href="" class="lh-topbar__url" target="_blank" rel="noopener"></a>
 
     <div class="lh-tools">
-      <button class="report-icon report-icon--share lh-tools__button" title="Export report">
+      <button class="report-icon report-icon--share lh-tools__button" title="Tools menu" aria-label="Toggle report tools menu">
         <svg width="100%" height="100%" viewBox="0 0 24 24">
             <path d="M0 0h24v24H0z" fill="none"/>
             <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
@@ -494,7 +494,7 @@ limitations under the License.
 <template id="tmpl-lh-footer">
   <style>
     .lh-footer {
-      padding: var(--section-indent) calc(var(--default-padding) * 2);
+      padding: var(--footer-padding-vertical) calc(var(--default-padding) * 2);
       max-width: var(--report-width);
       margin: 0 auto;
     }
@@ -502,8 +502,8 @@ limitations under the License.
       text-align: center;
     }
     .lh-env__title {
-      font-size: var(--score-title-font-size-big);
-      line-height: var(--score-title-line-height-big);
+      font-size: var(--env-item-font-size-big);
+      line-height: var(--env-item-line-height-big);
       text-align: center;
       padding: var(--score-container-padding);
     }
@@ -516,11 +516,11 @@ limitations under the License.
       padding: 0;
     }
     .lh-env__items .lh-env__item:nth-child(2n) {
-      background: var(--env-item-bg);
+      background-color: var(--env-item-background-color);
     }
     .lh-env__item {
       display: flex;
-      padding: var(--env-tem-padding);
+      padding: var(--env-item-padding);
       position: relative;
     }
     span.lh-env__name {
@@ -584,7 +584,7 @@ limitations under the License.
     }
 
     .lh-gauge--pwa__disc {
-      fill: var(--color-black-200);
+      fill: var(--color-gray-200);
     }
 
     .lh-gauge--pwa__logo--primary-color {
@@ -782,8 +782,8 @@ limitations under the License.
 <template id="tmpl-lh-3p-filter">
   <style>
     .lh-3p-filter {
-      background: var(--lh-table-higlight-bg);
-      color: var(--medium-75-gray);
+      background-color: var(--table-higlight-background-color);
+      color: var(--color-gray-600);
       float: right;
       padding: 6px;
     }
@@ -815,8 +815,8 @@ limitations under the License.
           overflow: hidden;
           padding: 10px;
           border-bottom: none;
-          color: var(--subheader-color);
-          background: var(--medium-50-gray);
+          color: var(--snippet-color);
+          background-color: var(--snippet-background-color);
           border: 1px solid var(--report-secondary-border-color);
         }
         .lh-snippet__title {
@@ -898,8 +898,8 @@ limitations under the License.
           display: inline-block;
           vertical-align: middle;
           margin-right: 4px;
-          width: var(--score-shape-size);
-          height: var(--score-shape-size);
+          width: var(--score-icon-size);
+          height: var(--score-icon-size);
           background-image: var(--fail-icon-url);
         }
         .lh-snippet__line-number {
@@ -909,7 +909,7 @@ limitations under the License.
           font-family: monospace;
           padding-right: 5px;
           margin-right: 5px;
-          color: var(--medium-75-gray);
+          color: var(--color-gray-600);
           user-select: none;
         }
       </style>
diff --git a/lighthouse-core/scripts/download-chrome.sh b/lighthouse-core/scripts/download-chrome.sh
index 5c073cd7d..aeeae3e90 100755
--- a/lighthouse-core/scripts/download-chrome.sh
+++ b/lighthouse-core/scripts/download-chrome.sh
@@ -14,12 +14,12 @@ else
   url="https://download-chromium.appspot.com/dl/Linux_x64?type=snapshots"
 fi
 
-if [ x"$LIGHTHOUSE_CHROMIUM_PATH" == x ]; then
-  echo "Error: Environment variable LIGHTHOUSE_CHROMIUM_PATH not set"
+if [ x"$CHROME_PATH" == x ]; then
+  echo "Error: Environment variable CHROME_PATH not set"
   exit 1
 fi
 
-if [ -e "$LIGHTHOUSE_CHROMIUM_PATH" ]; then
+if [ -e "$CHROME_PATH" ]; then
   echo "cached chrome found"
 else
   wget "$url" --no-check-certificate -q -O chrome.zip && unzip -q chrome.zip
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 5e0e3279f..54f58ebd4 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -759,7 +759,8 @@
             "id": "font-display",
             "score": 1.0,
             "scoreDisplayMode": "binary",
-            "title": "All text remains visible during webfont loads"
+            "title": "All text remains visible during webfont loads",
+            "warnings": []
         },
         "font-size": {
             "description": "Font sizes less than 12px are too small to be legible and require mobile visitors to \u201cpinch to zoom\u201d in order to read. Strive to have >60% of page text \u226512px. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/font-sizes).",
diff --git a/readme.md b/readme.md
index 50d447e5c..0f8399ca8 100644
--- a/readme.md
+++ b/readme.md
@@ -315,9 +315,10 @@ Other awesome open source projects that use Lighthouse.
 * **[lighthouse-cron](https://github.com/thearegee/lighthouse-cron)** - Cron multiple batch Lighthouse audits and emit results for sending to remote server.
 * **[lighthouse-gh-reporter](https://github.com/carlesnunez/lighthouse-gh-reporter)** - Run Lighthouse in CI and report back in a comment on your pull requests
 * **[lighthouse-hue](https://github.com/ebidel/lighthouse-hue)** - set the color of Philips Hue lights based on a Lighthouse score
+* **[lighthouse-jest-example](https://github.com/justinribeiro/lighthouse-jest-example)** - gather performance metrics via Lighthouse and assert results with Jest; uses Puppeteer to start Chrome with network emulation settings defined by WebPageTest.
 * **[lighthouse-lambda](https://github.com/joytocode/lighthouse-lambda)** - Run Lighthouse on AWS Lambda with prebuilt stable desktop Headless Chrome.
 * **[lighthouse-magic-light](https://github.com/manekinekko/lighthouse-magic-light)** set the color of the MagicLight Bluetooth Smart Light Bulb based on Lighthouse score
-* **[lighthouse-mocha-example](https://github.com/justinribeiro/lighthouse-mocha-example)** - gather performance metrics via Lighthouse and tests them in Mocha
+* **[lighthouse-mocha-example](https://github.com/rishichawda/lighthouse-mocha-example)** - Run Lighthouse performance tests with Mocha and chrome-launcher.
 * **[lighthouse-persist](https://github.com/foo-software/lighthouse-persist)** - Run Lighthouse and upload HTML reports to an AWS S3 bucket.
 * **[lighthouse4u](https://github.com/godaddy/lighthouse4u)** - LH4U provides Google Lighthouse as a service, surfaced by both a friendly UI+API, and backed by Elastic Search for easy querying and visualization.
 * **[performance-budgets](https://performance-budgets.netlify.com/)** - Easily assert Lighthouse budgets with Docker.
diff --git a/types/externs.d.ts b/types/externs.d.ts
index 94b229daf..ded65e95f 100644
--- a/types/externs.d.ts
+++ b/types/externs.d.ts
@@ -74,7 +74,7 @@ declare global {
       serverResponseTimeByOrigin: {[origin: string]: number};
     }
 
-    export type Locale = 'en-US'|'en'|'en-AU'|'en-GB'|'en-IE'|'en-SG'|'en-ZA'|'en-IN'|'ar-XB'|'ar'|'bg'|'bs'|'ca'|'cs'|'da'|'de'|'el'|'en-XA'|'es'|'fi'|'fil'|'fr'|'he'|'hi'|'hr'|'hu'|'gsw'|'id'|'in'|'it'|'iw'|'ja'|'ko'|'ln'|'lt'|'lv'|'mo'|'nl'|'nb'|'no'|'pl'|'pt'|'pt-PT'|'ro'|'ru'|'sk'|'sl'|'sr'|'sr-Latn'|'sv'|'ta'|'te'|'th'|'tl'|'tr'|'uk'|'vi'|'zh'|'zh-HK'|'zh-TW';
+    export type Locale = 'en-US'|'en'|'en-AU'|'en-GB'|'en-IE'|'en-SG'|'en-ZA'|'en-IN'|'ar-XB'|'ar'|'bg'|'bs'|'ca'|'cs'|'da'|'de'|'el'|'en-XA'|'es'|'es-419'|'es-AR'|'es-BO'|'es-BR'|'es-BZ'|'es-CL'|'es-CO'|'es-CR'|'es-CU'|'es-DO'|'es-EC'|'es-GT'|'es-HN'|'es-MX'|'es-NI'|'es-PA'|'es-PE'|'es-PR'|'es-PY'|'es-SV'|'es-US'|'es-UY'|'es-VE'|'fi'|'fil'|'fr'|'he'|'hi'|'hr'|'hu'|'gsw'|'id'|'in'|'it'|'iw'|'ja'|'ko'|'ln'|'lt'|'lv'|'mo'|'nl'|'nb'|'no'|'pl'|'pt'|'pt-PT'|'ro'|'ru'|'sk'|'sl'|'sr'|'sr-Latn'|'sv'|'ta'|'te'|'th'|'tl'|'tr'|'uk'|'vi'|'zh'|'zh-HK'|'zh-TW';
 
     export type OutputMode = 'json' | 'html' | 'csv';
 


Test patch:
diff --git a/lighthouse-cli/test/smokehouse/error-expectations.js b/lighthouse-cli/test/smokehouse/error-expectations.js
index 0d2e40ac4..7f0f9bc80 100644
--- a/lighthouse-cli/test/smokehouse/error-expectations.js
+++ b/lighthouse-cli/test/smokehouse/error-expectations.js
@@ -27,10 +27,10 @@ module.exports = [
       finalUrl: 'https://expired.badssl.com/',
       audits: {},
       // TODO: runtimeError only exists because of selection of audits.
-      runtimeError: {code: 'FAILED_DOCUMENT_REQUEST'},
+      runtimeError: {code: 'INSECURE_DOCUMENT_REQUEST'},
     },
     artifacts: {
-      ViewportDimensions: {code: 'FAILED_DOCUMENT_REQUEST'},
+      ViewportDimensions: {code: 'INSECURE_DOCUMENT_REQUEST'},
     },
   },
 ];
diff --git a/lighthouse-core/test/audits/font-display-test.js b/lighthouse-core/test/audits/font-display-test.js
index 36b674050..f3dfad9d5 100644
--- a/lighthouse-core/test/audits/font-display-test.js
+++ b/lighthouse-core/test/audits/font-display-test.js
@@ -6,7 +6,6 @@
 'use strict';
 
 const FontDisplayAudit = require('../../audits/font-display.js');
-const assert = require('assert');
 const networkRecordsToDevtoolsLog = require('../network-records-to-devtools-log.js');
 
 /* eslint-env jest */
@@ -73,8 +72,9 @@ describe('Performance: Font Display audit', () => {
       {url: networkRecords[1].url, wastedMs: 3000},
       {url: networkRecords[2].url, wastedMs: 1000},
     ];
-    assert.strictEqual(result.score, 0);
+    expect(result.score).toEqual(0);
     expect(result.details.items).toEqual(items);
+    expect(result.warnings).toEqual([]);
   });
 
   it('resolves URLs relative to stylesheet URL when available', async () => {
@@ -129,8 +129,9 @@ describe('Performance: Font Display audit', () => {
     ];
 
     const result = await FontDisplayAudit.audit(getArtifacts(), context);
-    assert.strictEqual(result.score, 1);
+    expect(result.score).toEqual(1);
     expect(result.details.items).toEqual([]);
+    expect(result.warnings).toEqual([]);
   });
 
   it('passes when all fonts have a correct font-display rule', async () => {
@@ -180,8 +181,9 @@ describe('Performance: Font Display audit', () => {
     ];
 
     const result = await FontDisplayAudit.audit(getArtifacts(), context);
-    assert.strictEqual(result.score, 1);
+    expect(result.score).toEqual(1);
     expect(result.details.items).toEqual([]);
+    expect(result.warnings).toEqual([]);
   });
 
   it('should handle real-world font-face declarations', async () => {
@@ -220,7 +222,7 @@ describe('Performance: Font Display audit', () => {
     ];
 
     const result = await FontDisplayAudit.audit(getArtifacts(), context);
-    assert.strictEqual(result.score, 0);
+    expect(result.score).toEqual(0);
     expect(result.details.items.map(item => item.url)).toEqual([
       'https://edition.i.cdn.cnn.com/.a/fonts/cnn/3.7.2/cnnclock-black.woff2',
       'https://registry.api.cnn.io/assets/fave/fonts/2.0.15/cnnsans-bold.woff',
@@ -228,6 +230,7 @@ describe('Performance: Font Display audit', () => {
       // 'https://example.com/foo/fonts/fontawesome-webfont.woff2?v=4.6.1',
       'https://fonts.gstatic.com/s/lato/v14/S6u9w4BMUTPHh50XSwiPGQ3q5d0.woff2',
     ]);
+    expect(result.warnings).toEqual([]);
   });
 
   it('handles varied font-display declarations', async () => {
@@ -259,7 +262,8 @@ describe('Performance: Font Display audit', () => {
 
     const result = await FontDisplayAudit.audit(getArtifacts(), context);
     expect(result.details.items).toEqual([]);
-    assert.strictEqual(result.score, 1);
+    expect(result.score).toEqual(1);
+    expect(result.warnings).toEqual([]);
   });
 
   it('handles custom source URLs from sourcemaps', async () => {
@@ -280,6 +284,57 @@ describe('Performance: Font Display audit', () => {
 
     const result = await FontDisplayAudit.audit(getArtifacts(), context);
     expect(result.details.items).toEqual([]);
-    assert.strictEqual(result.score, 1);
+    expect(result.score).toEqual(1);
+  });
+
+  it('should not flag a URL for which there is not @font-face at all', async () => {
+    // Sometimes the content does not come through, see https://github.com/GoogleChrome/lighthouse/issues/8493
+    stylesheet.content = ``;
+
+    networkRecords = [{
+      url: `https://example.com/foo/bar/font-0.woff`,
+      endTime: 2, startTime: 1,
+      resourceType: 'Font',
+    }];
+
+    const result = await FontDisplayAudit.audit(getArtifacts(), context);
+    expect(result.details.items).toEqual([]);
+    expect(result.score).toEqual(1);
+    expect(result.warnings).toHaveLength(1);
+    expect(result.warnings[0]).toBeDisplayString(/font-0.woff/);
+  });
+
+  it('should handle mixed content', async () => {
+    networkRecords = [{
+      url: `https://example.com/foo/bar/font-0.woff`,
+      endTime: 2, startTime: 1,
+      resourceType: 'Font',
+    }, {
+      url: `https://example.com/foo/bar/font-1.woff`,
+      endTime: 2, startTime: 1,
+      resourceType: 'Font',
+    }];
+
+    const artifacts = getArtifacts();
+    artifacts.CSSUsage.stylesheets = [
+      {content: '', header: {}},
+      {
+        content: `
+          @font-face {
+            /* try with " */
+            src: url("./font-0.woff");
+          }
+        `,
+        header: {},
+      },
+    ];
+    const result = await FontDisplayAudit.audit(artifacts, context);
+    expect(result.details.items).toEqual([{
+      url: `https://example.com/foo/bar/font-0.woff`,
+      wastedMs: 1000,
+    }]);
+    expect(result.score).toEqual(0);
+    expect(result.warnings).toHaveLength(1);
+    expect(result.warnings[0]).toBeDisplayString(/font-1.woff/);
   });
 });
diff --git a/lighthouse-core/test/gather/gather-runner-test.js b/lighthouse-core/test/gather/gather-runner-test.js
index f76f0f63f..234bd7fd7 100644
--- a/lighthouse-core/test/gather/gather-runner-test.js
+++ b/lighthouse-core/test/gather/gather-runner-test.js
@@ -850,6 +850,135 @@ describe('GatherRunner', function() {
     });
   });
 
+  describe('#getInterstitialError', () => {
+    it('passes when the page is loaded', () => {
+      const url = 'http://the-page.com';
+      const mainRecord = new NetworkRequest();
+      mainRecord.url = url;
+      expect(GatherRunner.getInterstitialError([mainRecord])).toBeUndefined();
+    });
+
+    it('passes when page fails to load normally', () => {
+      const url = 'http://the-page.com';
+      const mainRecord = new NetworkRequest();
+      mainRecord.url = url;
+      mainRecord.failed = true;
+      mainRecord.localizedFailDescription = 'foobar';
+      expect(GatherRunner.getInterstitialError([mainRecord])).toBeUndefined();
+    });
+
+    it('passes when page gets a generic interstitial but somehow also loads everything', () => {
+      // This case, AFAIK, is impossible, but we'll err on the side of not tanking the run.
+      const url = 'http://the-page.com';
+      const mainRecord = new NetworkRequest();
+      mainRecord.url = url;
+      const interstitialRecord = new NetworkRequest();
+      interstitialRecord.url = 'data:text/html;base64,abcdef';
+      interstitialRecord.documentURL = 'chrome-error://chromewebdata/';
+      const records = [mainRecord, interstitialRecord];
+      expect(GatherRunner.getInterstitialError(records)).toBeUndefined();
+    });
+
+    it('fails when page gets a generic interstitial', () => {
+      const url = 'http://the-page.com';
+      const mainRecord = new NetworkRequest();
+      mainRecord.url = url;
+      mainRecord.failed = true;
+      mainRecord.localizedFailDescription = 'ERR_CONNECTION_RESET';
+      const interstitialRecord = new NetworkRequest();
+      interstitialRecord.url = 'data:text/html;base64,abcdef';
+      interstitialRecord.documentURL = 'chrome-error://chromewebdata/';
+      const records = [mainRecord, interstitialRecord];
+      const error = GatherRunner.getInterstitialError(records);
+      expect(error.message).toEqual('CHROME_INTERSTITIAL_ERROR');
+      expect(error.code).toEqual('CHROME_INTERSTITIAL_ERROR');
+      expect(error.friendlyMessage).toBeDisplayString(/^Chrome prevented/);
+    });
+
+    it('fails when page gets a security interstitial', () => {
+      const url = 'http://the-page.com';
+      const mainRecord = new NetworkRequest();
+      mainRecord.url = url;
+      mainRecord.failed = true;
+      mainRecord.localizedFailDescription = 'net::ERR_CERT_COMMON_NAME_INVALID';
+      const interstitialRecord = new NetworkRequest();
+      interstitialRecord.url = 'data:text/html;base64,abcdef';
+      interstitialRecord.documentURL = 'chrome-error://chromewebdata/';
+      const records = [mainRecord, interstitialRecord];
+      const error = GatherRunner.getInterstitialError(records);
+      expect(error.message).toEqual('INSECURE_DOCUMENT_REQUEST');
+      expect(error.code).toEqual('INSECURE_DOCUMENT_REQUEST');
+      expect(error.friendlyMessage).toBeDisplayString(/valid security certificate/);
+      expect(error.friendlyMessage).toBeDisplayString(/net::ERR_CERT_COMMON_NAME_INVALID/);
+    });
+  });
+
+  describe('#getPageLoadError', () => {
+    let navigationError;
+
+    beforeEach(() => {
+      navigationError = new Error('NAVIGATION_ERROR');
+    });
+
+    it('passes when the page is loaded', () => {
+      const passContext = {url: 'http://the-page.com', driver: {online: true}};
+      const mainRecord = new NetworkRequest();
+      const loadData = {networkRecords: [mainRecord]};
+      mainRecord.url = passContext.url;
+      const error = GatherRunner.getPageLoadError(passContext, loadData, undefined);
+      expect(error).toBeUndefined();
+    });
+
+    it('passes when the page is offline', () => {
+      const passContext = {url: 'http://the-page.com', driver: {online: false}};
+      const mainRecord = new NetworkRequest();
+      const loadData = {networkRecords: [mainRecord]};
+      mainRecord.url = passContext.url;
+      mainRecord.failed = true;
+
+      const error = GatherRunner.getPageLoadError(passContext, loadData, undefined);
+      expect(error).toBeUndefined();
+    });
+
+    it('fails with interstitial error first', () => {
+      const passContext = {url: 'http://the-page.com', driver: {online: true}};
+      const mainRecord = new NetworkRequest();
+      const interstitialRecord = new NetworkRequest();
+      const loadData = {networkRecords: [mainRecord, interstitialRecord]};
+
+      mainRecord.url = passContext.url;
+      mainRecord.failed = true;
+      interstitialRecord.url = 'data:text/html;base64,abcdef';
+      interstitialRecord.documentURL = 'chrome-error://chromewebdata/';
+
+      const error = GatherRunner.getPageLoadError(passContext, loadData, navigationError);
+      expect(error.message).toEqual('CHROME_INTERSTITIAL_ERROR');
+    });
+
+    it('fails with network error next', () => {
+      const passContext = {url: 'http://the-page.com', driver: {online: true}};
+      const mainRecord = new NetworkRequest();
+      const loadData = {networkRecords: [mainRecord]};
+
+      mainRecord.url = passContext.url;
+      mainRecord.failed = true;
+
+      const error = GatherRunner.getPageLoadError(passContext, loadData, navigationError);
+      expect(error.message).toEqual('FAILED_DOCUMENT_REQUEST');
+    });
+
+    it('fails with nav error last', () => {
+      const passContext = {url: 'http://the-page.com', driver: {online: true}};
+      const mainRecord = new NetworkRequest();
+      const loadData = {networkRecords: [mainRecord]};
+
+      mainRecord.url = passContext.url;
+
+      const error = GatherRunner.getPageLoadError(passContext, loadData, navigationError);
+      expect(error.message).toEqual('NAVIGATION_ERROR');
+    });
+  });
+
   describe('artifact collection', () => {
     // Make sure our gatherers never execute in parallel
     it('runs gatherer lifecycle methods strictly in sequence', async () => {
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index 088cbea10..d9a6deefb 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -796,6 +796,7 @@
       "description": "Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading. [Learn more](https://developers.google.com/web/updates/2016/02/font-display).",
       "score": 1,
       "scoreDisplayMode": "binary",
+      "warnings": [],
       "details": {
         "type": "table",
         "headings": [],