Repository: GoogleChrome/lighthouse
Pull request #8893 (2019-05-07 04:54:53)

report(redesign): move gauge styles to main stylesheet
#8185 

Fixes: 
![image](https://user-images.githubusercontent.com/4071474/57272633-5c998800-7049-11e9-975d-62c1363daa98.png)

The PWA gauge depends on many style declarations defined in the template for regular gauges. When there are no regular gauges, that `style` element never gets add. I moved all those styles to the main stylesheet to cope.


Patch:
diff --git a/.npmignore b/.npmignore
index 9b7254d0f..7c42d9dc7 100644
--- a/.npmignore
+++ b/.npmignore
@@ -7,6 +7,7 @@ assets/
 build/
 coverage/
 dist/
+proto/
 
 lighthouse-core/scripts/*
 lighthouse-core/test/
diff --git a/changelog.md b/changelog.md
index 3585359f3..8f25acad8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,151 @@
+ <a name="5.0.0"></a>
+# 5.0.0 (2019-05-07)
+[Full Changelog](https://github.com/googlechrome/lighthouse/compare/v4.3.0...v5.0.0)
+
+## Major new features
+
+* [Stack packs](https://github.com/GoogleChrome/lighthouse-stack-packs) are a new way to present stack-specific advice wihin the Lighthouse report. This initial version detects if a site is running on WordPress and adds additional information to the report on how to take advantage of Wordpress plugins and configuration to improve performance. ([#7243](https://github.com/googlechrome/lighthouse/pull/7243), [#8169](https://github.com/googlechrome/lighthouse/pull/8169), [#8633](https://github.com/googlechrome/lighthouse/pull/8633), [#8536](https://github.com/googlechrome/lighthouse/pull/8536), [#8778](https://github.com/googlechrome/lighthouse/pull/8778))
+* [Performance budgets](https://developers.google.com/web/tools/lighthouse/audits/budgets) are a new feature of Lighthouse to help prevent your site performance from regressing over time. Set budgets for the resources a page should load and have Lighthouse alert if the budgets aren't met. ([#8539](https://github.com/googlechrome/lighthouse/pull/8539), [#8522](https://github.com/googlechrome/lighthouse/pull/8522), [#8727](https://github.com/googlechrome/lighthouse/pull/8727), [#8709](https://github.com/googlechrome/lighthouse/pull/8709), [#8427](https://github.com/googlechrome/lighthouse/pull/8427), [#8708](https://github.com/googlechrome/lighthouse/pull/8708))
+* The HTML report has been redesigned!
+
+## New Audits
+
+* `apple-touch-icon` PWA audit ([#8857](https://github.com/googlechrome/lighthouse/pull/8857))
+* `max-potential-fid` (new in HTML report) ([#8729](https://github.com/googlechrome/lighthouse/pull/8729), [#8817](https://github.com/googlechrome/lighthouse/pull/8817))
+* `performance-budget` ([#8539](https://github.com/googlechrome/lighthouse/pull/8539))
+* `resource-summary` ([#8522](https://github.com/googlechrome/lighthouse/pull/8522))
+
+## Breaking changes
+* Node 10 (LTS) or later is now required. ([#8117](https://github.com/googlechrome/lighthouse/pull/8117))
+* `rawValue` has been removed from audit results. If the audit had a meaningful numeric result (like a metric), this result will now be on `numericValue`. ([#8343](https://github.com/googlechrome/lighthouse/pull/8343), [#8385](https://github.com/googlechrome/lighthouse/pull/8385), [#8421](https://github.com/googlechrome/lighthouse/pull/8421))
+* The accessibility category scoring has been reweighted based on severity. ([#8823](https://github.com/googlechrome/lighthouse/pull/8823))
+* FCP accuracy has improved (in 'simulation' throttling) and may noticeably affect scores. ([#7513](https://github.com/googlechrome/lighthouse/pull/7513))
+* The deprecated `--disable-device-emulation` flag has been removed. Use `--emulated-form-factor`. ([#8289](https://github.com/googlechrome/lighthouse/pull/8289), [#8334](https://github.com/googlechrome/lighthouse/pull/8334))
+* The `dom-size` now ignores elements in `<head>` and limits scoring to only DOM nodes in the body. ([#7241](https://github.com/googlechrome/lighthouse/pull/7241))
+* The `diagnostics` audit details have had their `type` value changed to `debugdata` ([#8298](https://github.com/googlechrome/lighthouse/pull/8298))
+* The format of the `screenshot-thumbnails` screenshots has changed. They are now a full image data URL. ([#8299](https://github.com/googlechrome/lighthouse/pull/8299))
+
+## New contributors!
+
+Thanks to @arnabsen, @housseindjirdeh, @MarkelFe, @muuvmuuv, and @Meggin for their first contributions!
+
+
+## Core
+
+* a11y: disable unused axe rules ([#8373](https://github.com/googlechrome/lighthouse/pull/8373))
+* artifacts: create a PublicGathererArtifacts type ([#8382](https://github.com/googlechrome/lighthouse/pull/8382))
+* runner: only pass requiredArtifacts to audits ([#8760](https://github.com/googlechrome/lighthouse/pull/8760))
+* crc: redirects leading to iframe shouldn't be marked as critical ([#6704](https://github.com/googlechrome/lighthouse/pull/6704))
+* driver: enable async stacks ([#5504](https://github.com/googlechrome/lighthouse/pull/5504))
+* `font-display`: do not use invalid sourceURLs ([#8535](https://github.com/googlechrome/lighthouse/pull/8535))
+* jsonld: structured data validation updates ([#8137](https://github.com/googlechrome/lighthouse/pull/8137))
+* rect-helpers: make getBoundingRect take an array of rects ([#8789](https://github.com/googlechrome/lighthouse/pull/8789))
+* seo: support spanish in `link-text` audit ([#7547](https://github.com/googlechrome/lighthouse/pull/7547))
+* filter blob urls from more audits ([#8724](https://github.com/googlechrome/lighthouse/pull/8724))
+* reword insecure error message ([#8530](https://github.com/googlechrome/lighthouse/pull/8530))
+* seo: add `image-alt` to SEO category ([#8407](https://github.com/googlechrome/lighthouse/pull/8407))
+* add devtoolsNodePath property to ScriptElements artifact ([#8133](https://github.com/googlechrome/lighthouse/pull/8133))
+* always run axe gatherer last ([#8216](https://github.com/googlechrome/lighthouse/pull/8216))
+
+## Report
+
+* redesign: three dots for export button ([#8629](https://github.com/googlechrome/lighthouse/pull/8629))
+* redesign: design review feedback ([#8785](https://github.com/googlechrome/lighthouse/pull/8785))
+* redesign: whitespace, font weights ([#8820](https://github.com/googlechrome/lighthouse/pull/8820), [#8891](https://github.com/googlechrome/lighthouse/pull/8891), [#8592](https://github.com/googlechrome/lighthouse/pull/8592), [#8591](https://github.com/googlechrome/lighthouse/pull/8591), [#8531](https://github.com/googlechrome/lighthouse/pull/8531), [#8528](https://github.com/googlechrome/lighthouse/pull/8528))
+* make v5 renderer compatible with v3 and v4 LHRs ([#8822](https://github.com/googlechrome/lighthouse/pull/8822))
+* redesign: fireworks ([#8660](https://github.com/googlechrome/lighthouse/pull/8660))
+* redesign: add toggle control to show metric descriptions ([#8661](https://github.com/googlechrome/lighthouse/pull/8661), [#8844](https://github.com/googlechrome/lighthouse/pull/8844))
+* add table filter for third-party urls ([#6351](https://github.com/googlechrome/lighthouse/pull/6351))
+* redesign: remove units from table column headings ([#8658](https://github.com/googlechrome/lighthouse/pull/8658)))
+* redesign: hide sparkline in mobile ([#8562](https://github.com/googlechrome/lighthouse/pull/8562))
+* redesign: add dark theme ([#8425](https://github.com/googlechrome/lighthouse/pull/8425), [#8843](https://github.com/googlechrome/lighthouse/pull/8843), [#8845](https://github.com/googlechrome/lighthouse/pull/8845), [#8842](https://github.com/googlechrome/lighthouse/pull/8842))
+* redesign: add sticky scores header ([#8524](https://github.com/googlechrome/lighthouse/pull/8524), [#8883](https://github.com/googlechrome/lighthouse/pull/8883))
+* redesign: runtime settings footer ([#8229](https://github.com/googlechrome/lighthouse/pull/8229))
+* add missing </div> ([#8240](https://github.com/googlechrome/lighthouse/pull/8240))
+* redesign: new gauges, score scale, category headers, variables ([#8121](https://github.com/googlechrome/lighthouse/pull/8121), [#8300](https://github.com/googlechrome/lighthouse/pull/8300), [#8329](https://github.com/googlechrome/lighthouse/pull/8329), [#8307](https://github.com/googlechrome/lighthouse/pull/8307), [#8222](https://github.com/googlechrome/lighthouse/pull/8222), [#8529](https://github.com/googlechrome/lighthouse/pull/8529), [#8532](https://github.com/googlechrome/lighthouse/pull/8532), [#8590](https://github.com/googlechrome/lighthouse/pull/8590), [#8653](https://github.com/googlechrome/lighthouse/pull/8653), [#8720](https://github.com/googlechrome/lighthouse/pull/8720), [#8722](https://github.com/googlechrome/lighthouse/pull/8722), [#8763](https://github.com/googlechrome/lighthouse/pull/8763), [#8872](https://github.com/googlechrome/lighthouse/pull/8872))
+* extract init() function from report-template ([#8228](https://github.com/googlechrome/lighthouse/pull/8228))
+
+## Clients
+
+* devtools: remove ascii-encoded asset requirement ([#8456](https://github.com/googlechrome/lighthouse/pull/8456), [#8637](https://github.com/googlechrome/lighthouse/pull/8637))
+* lr: increase Page.getAppManifest timeout to 10s ([#8350](https://github.com/googlechrome/lighthouse/pull/8350))
+* lr: apply, revert Lightrider timings to NetworkRequests ([#8109](https://github.com/googlechrome/lighthouse/pull/8109), [#8752](https://github.com/googlechrome/lighthouse/pull/8752))
+* psi: pass score scale element to psi ([#8827](https://github.com/googlechrome/lighthouse/pull/8827))
+* lr: build report-generator-bundle ([#8197](https://github.com/googlechrome/lighthouse/pull/8197))
+
+## Docs
+
+* update throttling ([#8854](https://github.com/googlechrome/lighthouse/pull/8854))
+* add plugin guide ([#8728](https://github.com/googlechrome/lighthouse/pull/8728))
+* readme: add mention of free tier of Foo integration ([#8160](https://github.com/googlechrome/lighthouse/pull/8160))
+* readme: remove link to archived lighthouse-security project ([#8141](https://github.com/googlechrome/lighthouse/pull/8141))
+
+## CLI
+
+* always handle chrome connection errors ([#8583](https://github.com/googlechrome/lighthouse/pull/8583))
+* document that CSV is output to file ([#8520](https://github.com/googlechrome/lighthouse/pull/8520))
+
+## Deps
+
+* chrome-launcher@0.10.7 ([#8781](https://github.com/googlechrome/lighthouse/pull/8781))
+* axe-core@3.2.2 ([#8370](https://github.com/googlechrome/lighthouse/pull/8370))
+
+## Tests
+
+* lightwallet: add perf-budget smoke test ([#8853](https://github.com/googlechrome/lighthouse/pull/8853))
+* lightwallet: add budget to sample artifacts config ([#8783](https://github.com/googlechrome/lighthouse/pull/8783), [#8870](https://github.com/googlechrome/lighthouse/pull/8870))
+* add tap targets to dobetterweb sample page ([#8803](https://github.com/googlechrome/lighthouse/pull/8803))
+* make update:sample-artifacts work for a single artifact type ([#8802](https://github.com/googlechrome/lighthouse/pull/8802))
+* smokehouse: capture microtask regression ([#8379](https://github.com/googlechrome/lighthouse/pull/8379))
+* smokehouse: support artifacts assertions in smoke tests ([#8044](https://github.com/googlechrome/lighthouse/pull/8044))
+* smokehouse: fix tests from sharing artifacts ([#8897](https://github.com/googlechrome/lighthouse/pull/8897))
+* smokehouse: change metric assertions from score to numericValue ([#8805](https://github.com/googlechrome/lighthouse/pull/8805))
+* fix sample_v2 generation, proto errors ([#8605](https://github.com/googlechrome/lighthouse/pull/8605))
+
+## I18n
+
+* import tamil translated strings ([#8886](https://github.com/googlechrome/lighthouse/pull/8886))
+* SEO group titles and descriptions ([#8719](https://github.com/googlechrome/lighthouse/pull/8719))
+* stack pack translations ([#8154](https://github.com/googlechrome/lighthouse/pull/8154), [#8415](https://github.com/googlechrome/lighthouse/pull/8415))
+* update seo group titles and descriptions ([#8378](https://github.com/googlechrome/lighthouse/pull/8378))
+
+## Misc
+
+* proto: require protobuf 3.7.1, add stricter audit details test ([#8863](https://github.com/googlechrome/lighthouse/pull/8863), [#8867](https://github.com/googlechrome/lighthouse/pull/8867))
+* proto: remove trailing whitespace; add contributing help ([#8818](https://github.com/googlechrome/lighthouse/pull/8818))
+* fix roundtrip sample_v2.json ([#8815](https://github.com/googlechrome/lighthouse/pull/8815))
+* rename release scripts properly ([#8751](https://github.com/googlechrome/lighthouse/pull/8751))
+* simplify LH.Config settings types ([#8630](https://github.com/googlechrome/lighthouse/pull/8630))
+* add correct listitem.js @fileoverview ([#8586](https://github.com/googlechrome/lighthouse/pull/8586))
+* update sample_v2 artifacts ([#8243](https://github.com/googlechrome/lighthouse/pull/8243))
+* ci: build report and deploy to now.sh on every commit ([#8194](https://github.com/googlechrome/lighthouse/pull/8194))
+* fix bad auto-merge ([#8201](https://github.com/googlechrome/lighthouse/pull/8201))
+* update collected i18n string ([#8158](https://github.com/googlechrome/lighthouse/pull/8158))
+* `--help`: remove screenshots mention from `--save-assets` ([#8263](https://github.com/googlechrome/lighthouse/pull/8263))
+* lightwallet: update budget link ([#8871](https://github.com/googlechrome/lighthouse/pull/8871))
+* fix common typos ([#8726](https://github.com/googlechrome/lighthouse/pull/8726))
+
+ <a name="4.3.1"></a>
+# 4.3.1 (2019-04-30)
+[Full Changelog](https://github.com/googlechrome/lighthouse/compare/v4.3.0...v4.3.1)
+
+## Core
+
+* support traces missing TracingStartedInBrowser ([#7122](https://github.com/googlechrome/lighthouse/pull/7122))
+* driver: only fail security state if scheme is not cryptographic ([#8338](https://github.com/googlechrome/lighthouse/pull/8338))
+
+## Clients
+
+* extension: remove github link in favor of copy to clipboard ([#8294](https://github.com/googlechrome/lighthouse/pull/8294))
+
+## Deps
+
+* snyk: update snyk snapshot ([#8354](https://github.com/googlechrome/lighthouse/pull/8354))
+
+## Misc
+
+* add releasing scripts ([#8387](https://github.com/googlechrome/lighthouse/pull/8387))
+
  <a name="4.3.0"></a>
 # 4.3.0 (2019-04-08)
 [Full Changelog](https://github.com/googlechrome/lighthouse/compare/4.2.0...4.3.0)
diff --git a/clients/extension/manifest.json b/clients/extension/manifest.json
index 53738e023..6957976df 100644
--- a/clients/extension/manifest.json
+++ b/clients/extension/manifest.json
@@ -1,7 +1,7 @@
 {
   "name": "__MSG_appName__",
-  "version": "4.3.0",
-  "minimum_chrome_version": "66",
+  "version": "5.0.0",
+  "minimum_chrome_version": "72",
   "manifest_version": 2,
   "description": "__MSG_appDescription__",
   "icons": {
diff --git a/docs/plugins.md b/docs/plugins.md
index 2ef0cc331..f778be202 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -350,5 +350,5 @@ Most artifacts will try to represent as truthfully as possible what was observed
 
 ## Examples
 
-- [Google AdSpeed Insights](https://github.com/googleads/ad-speed-insights) - a well-written, but complex, plugin
+- [Publisher Ads Audits](https://github.com/googleads/pub-ads-lighthouse-plugin) - a well-written, but complex, plugin
 - [Lighthouse Plugin Recipe](./recipes/lighthouse-plugin-example)
diff --git a/lighthouse-core/config/budget.js b/lighthouse-core/config/budget.js
index e788e8f49..0f5d1553a 100644
--- a/lighthouse-core/config/budget.js
+++ b/lighthouse-core/config/budget.js
@@ -46,6 +46,22 @@ class Budget {
     }
   }
 
+  /**
+   * Asserts that `strings` has no duplicate strings in it, throwing an error if
+   * it does. `arrayName` is included for nicer logging.
+   * @param {Array<string>} strings
+   * @param {string} arrayName
+   */
+  static assertNoDuplicateStrings(strings, arrayName) {
+    const foundStrings = new Set();
+    for (const string of strings) {
+      if (foundStrings.has(string)) {
+        throw new Error(`${arrayName} has duplicate entry of type '${string}'`);
+      }
+      foundStrings.add(string);
+    }
+  }
+
   /**
    * @param {Record<string, unknown>} resourceBudget
    * @return {LH.Budget.ResourceBudget}
@@ -94,7 +110,7 @@ class Budget {
       'first-cpu-idle',
       'interactive',
       'first-meaningful-paint',
-      'estimated-input-latency',
+      'max-potential-fid',
     ];
     // Assume metric is an allowed string, throw if not.
     if (!validTimingMetrics.includes(/** @type {LH.Budget.TimingMetric} */ (metric))) {
@@ -136,18 +152,24 @@ class Budget {
 
       if (isArrayOfUnknownObjects(resourceSizes)) {
         budget.resourceSizes = resourceSizes.map(Budget.validateResourceBudget);
+        Budget.assertNoDuplicateStrings(budget.resourceSizes.map(r => r.resourceType),
+          `budgets[${index}].resourceSizes`);
       } else if (resourceSizes !== undefined) {
         throw new Error(`Invalid resourceSizes entry in budget at index ${index}`);
       }
 
       if (isArrayOfUnknownObjects(resourceCounts)) {
         budget.resourceCounts = resourceCounts.map(Budget.validateResourceBudget);
+        Budget.assertNoDuplicateStrings(budget.resourceCounts.map(r => r.resourceType),
+          `budgets[${index}].resourceCounts`);
       } else if (resourceCounts !== undefined) {
         throw new Error(`Invalid resourceCounts entry in budget at index ${index}`);
       }
 
       if (isArrayOfUnknownObjects(timings)) {
         budget.timings = timings.map(Budget.validateTimingBudget);
+        Budget.assertNoDuplicateStrings(budget.timings.map(r => r.metric),
+          `budgets[${index}].timings`);
       } else if (timings !== undefined) {
         throw new Error(`Invalid timings entry in budget at index ${index}`);
       }
diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js
index 813ae2024..928dc4742 100644
--- a/lighthouse-core/report/html/renderer/report-renderer.js
+++ b/lighthouse-core/report/html/renderer/report-renderer.js
@@ -242,17 +242,6 @@ class ReportRenderer {
       wrapper.appendChild(renderer.render(category, report.categoryGroups));
     }
 
-    // Fireworks
-    const scoresAll100 = report.reportCategories.every(cat => cat.score === 1);
-    if (!this._dom.isDevTools() && scoresAll100) {
-      const scoresContainer = this._dom.find('.lh-scores-container', headerContainer);
-      scoresContainer.classList.add('score100');
-      this._dom._document.body.classList.add('dark');
-      scoresContainer.addEventListener('click', _ => {
-        scoresContainer.classList.toggle('fireworks-paused');
-      });
-    }
-
     if (scoreHeader) {
       const scoreGauges =
         this._renderScoreGauges(report, categoryRenderer, specificCategoryRenderers);
diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js
index e0733f3b5..2458fa5e6 100644
--- a/lighthouse-core/report/html/renderer/report-ui-features.js
+++ b/lighthouse-core/report/html/renderer/report-ui-features.js
@@ -89,7 +89,27 @@ class ReportUIFeatures {
     this._document.addEventListener('keyup', this.onKeyUp);
     this._document.addEventListener('copy', this.onCopy);
     const topbarLogo = this._dom.find('.lh-topbar__logo', this._document);
-    topbarLogo.addEventListener('click', this._toggleDarkTheme);
+    topbarLogo.addEventListener('click', () => this._toggleDarkTheme());
+
+    let turnOffTheLights = false;
+    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
+      turnOffTheLights = true;
+    }
+
+    // Fireworks.
+    const scoresAll100 = Object.values(report.categories).every(cat => cat.score === 1);
+    if (!this._dom.isDevTools() && scoresAll100) {
+      turnOffTheLights = true;
+      const scoresContainer = this._dom.find('.lh-scores-container', this._document);
+      scoresContainer.classList.add('score100');
+      scoresContainer.addEventListener('click', _ => {
+        scoresContainer.classList.toggle('fireworks-paused');
+      });
+    }
+
+    if (turnOffTheLights) {
+      this._toggleDarkTheme(true);
+    }
 
     // There is only a sticky header when at least 2 categories are present.
     if (Object.keys(this.json.categories).length >= 2) {
@@ -295,8 +315,7 @@ class ReportUIFeatures {
    */
   onExportButtonClick(e) {
     e.preventDefault();
-    const el = /** @type {Element} */ (e.target);
-    el.classList.toggle('active');
+    this.exportButton.classList.toggle('active');
     this._document.addEventListener('keydown', this.onKeyDown);
   }
 
@@ -513,9 +532,10 @@ class ReportUIFeatures {
 
   /**
    * @private
+   * @param {boolean} [force]
    */
-  _toggleDarkTheme() {
-    this._document.body.classList.toggle('dark');
+  _toggleDarkTheme(force) {
+    this._document.body.classList.toggle('dark', force);
   }
 
   _updateStickyHeaderOnScroll() {
diff --git a/lighthouse-core/report/html/report-styles.css b/lighthouse-core/report/html/report-styles.css
index ea5205614..7e9703b35 100644
--- a/lighthouse-core/report/html/report-styles.css
+++ b/lighthouse-core/report/html/report-styles.css
@@ -63,6 +63,8 @@
   --lh-score-highlight-bg: hsla(0, 0%, 90%, 0.2);
   --lh-score-icon-background-size: 24px;
   --lh-group-icon-background-size: var(--lh-score-icon-background-size);
+  --lh-export-icon-size: var(--lh-score-icon-background-size);
+  --lh-export-icon-color: var(--medium-75-gray);
   --lh-score-margin: 12px;
   --lh-table-header-bg: #f8f9fa;
   --lh-table-higlight-bg: hsla(0, 0%, 75%, 0.1);
@@ -369,9 +371,6 @@
   pointer-events: none;
 }
 
-.report-icon--share {
-  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></svg>');
-}
 .report-icon--print {
   background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/><path fill="none" d="M0 0h24v24H0z"/></svg>');
 }
@@ -1023,10 +1022,134 @@
   border: 0;
 }
 
+/* Gauge */
+
+.lh-gauge__wrapper--pass {
+  color: var(--color-pass);
+  fill: var(--color-pass);
+  stroke: var(--color-pass);
+}
+
+.lh-gauge__wrapper--average {
+  color: var(--color-average);
+  fill: var(--color-average);
+  stroke: var(--color-average);
+}
+
+.lh-gauge__wrapper--fail {
+  color: var(--color-fail);
+  fill: var(--color-fail);
+  stroke: var(--color-fail);
+}
+
+.lh-gauge {
+  stroke-linecap: round;
+  width: var(--gauge-circle-size);
+  height: var(--gauge-circle-size);
+}
+
+.lh-category .lh-gauge {
+  --gauge-circle-size: var(--gauge-circle-size-big);
+}
+
+.lh-gauge-base {
+    opacity: 0.1;
+    stroke: var(--circle-background);
+    stroke-width: var(--circle-border-width);
+}
+
+.lh-gauge-arc {
+    fill: none;
+    stroke: var(--circle-color);
+    stroke-width: var(--circle-border-width);
+    animation: load-gauge var(--transition-length) ease forwards;
+    animation-delay: 250ms;
+}
+
+.lh-gauge__svg-wrapper {
+  position: relative;
+  height: var(--gauge-circle-size);
+}
+.lh-category .lh-gauge__svg-wrapper {
+  --gauge-circle-size: var(--gauge-circle-size-big);
+}
+
+/* The plugin badge overlay */
+.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-image: var(--plugin-icon-url);
+  background-repeat: no-repeat;
+  background-size: var(--plugin-icon-size);
+  background-position: 58% 50%;
+  content: "";
+  position: absolute;
+  right: -6px;
+  bottom: 0px;
+  display: block;
+  z-index: 100;
+  box-shadow: 0 0 4px rgba(0,0,0,.2);
+  border-radius: 25%;
+}
+.lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {
+  width: var(--plugin-badge-size-big);
+  height: var(--plugin-badge-size-big);
+}
+
+@keyframes load-gauge {
+  from { stroke-dasharray: 0 352; }
+}
+
+.lh-gauge__percentage {
+  width: 100%;
+  height: var(--inset-size);
+  position: absolute;
+  border-radius: inherit;
+  font-family: var(--monospace-font-family);
+  font-size: var(--score-number-font-size);
+  line-height: var(--score-number-font-size);
+  text-align: center;
+  top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 3);
+}
+
+.lh-category .lh-gauge__percentage {
+  --gauge-circle-size: var(--gauge-circle-size-big);
+  --score-number-font-size: var(--score-number-font-size-big);
+}
+
 .lh-gauge__wrapper {
+  position: relative;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  text-decoration: none;
   padding: var(--score-container-padding);
+
+  --circle-border-width: 8;
+  --transition-length: 1s;
+
+  /* Contain the layout style paint & layers during animation*/
+  contain: content;
+  will-change: opacity; /* Only using for layer promotion */
+}
+
+.lh-gauge__label {
+  font-size: var(--score-title-font-size);
+  line-height: var(--score-title-line-height);
+  margin-top: 10px;
+  text-align: center;
+  color: var(--body-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);
+  margin-top: 14px;
+}
+
+
 .lh-scores-header .lh-gauge__wrapper,
 .lh-scores-header .lh-gauge--pwa__wrapper,
 .lh-sticky-header .lh-gauge__wrapper,
diff --git a/lighthouse-core/report/html/templates.html b/lighthouse-core/report/html/templates.html
index 6df3186d4..cc8d7c798 100644
--- a/lighthouse-core/report/html/templates.html
+++ b/lighthouse-core/report/html/templates.html
@@ -178,7 +178,9 @@ limitations under the License.
       justify-content: center;
       background-color: var(--color-sticky-header-bg);
       border-bottom: 1px solid var(--color-black-200);
-      z-index: 1000;
+      padding-top: var(--score-container-padding);
+      padding-bottom: 4px;
+      z-index: 1;
       pointer-events: none;
       opacity: 0;
     }
@@ -247,20 +249,20 @@ limitations under the License.
       will-change: transform;
     }
     .lh-export__button {
-      border: 1px solid #dadada;
-      border-radius: 2px;
+      width: var(--lh-export-icon-size);
+      height: var(--lh-export-icon-size);
       cursor: pointer;
-      outline: none;
-      height: 23px;
-      width: 30px;
-      background-repeat: no-repeat;
-      background-size: 18px;
-      background-position: 50% 50%;
-      will-change: transform;
+      margin-right: 5px;
+    }
+    .lh-export__button svg {
+      fill: var(--lh-export-icon-color);
+    }
+    .dark .lh-export__button svg {
+      filter: invert(1);
     }
     .lh-export__button:focus,
     .lh-export__button.active {
-      box-shadow: 1px 1px 3px #ccc;
+      outline: none;
     }
     .lh-export__button.active + .lh-export__dropdown {
       opacity: 1;
@@ -346,7 +348,12 @@ limitations under the License.
     <a href="" class="lh-topbar__url" target="_blank" rel="noopener"></a>
 
     <div class="lh-export">
-      <button class="report-icon report-icon--share lh-export__button" title="Export report"></button>
+      <div class="report-icon report-icon--share lh-export__button" title="Export report">
+        <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"/>
+        </svg>
+      </div>
       <div class="lh-export__dropdown">
         <!-- TODO(i18n): localize export dropdown -->
         <a href="#" class="report-icon report-icon--print" data-action="print-summary">Print Summary</a>
@@ -532,132 +539,6 @@ limitations under the License.
 
 <!-- Lighthouse score gauge -->
 <template id="tmpl-lh-gauge">
-  <style>
-    .lh-gauge__wrapper--pass {
-      color: var(--color-pass);
-      fill: var(--color-pass);
-      stroke: var(--color-pass);
-    }
-
-    .lh-gauge__wrapper--average {
-      color: var(--color-average);
-      fill: var(--color-average);
-      stroke: var(--color-average);
-    }
-
-    .lh-gauge__wrapper--fail {
-      color: var(--color-fail);
-      fill: var(--color-fail);
-      stroke: var(--color-fail);
-    }
-
-    .lh-gauge {
-      stroke-linecap: round;
-      width: var(--gauge-circle-size);
-      height: var(--gauge-circle-size);
-    }
-
-    .lh-category .lh-gauge {
-      --gauge-circle-size: var(--gauge-circle-size-big);
-    }
-
-    .lh-gauge-base {
-        opacity: 0.1;
-        stroke: var(--circle-background);
-        stroke-width: var(--circle-border-width);
-    }
-
-    .lh-gauge-arc {
-        fill: none;
-        stroke: var(--circle-color);
-        stroke-width: var(--circle-border-width);
-        animation: load-gauge var(--transition-length) ease forwards;
-        animation-delay: 250ms;
-    }
-
-    .lh-gauge__svg-wrapper {
-      position: relative;
-      height: var(--gauge-circle-size);
-    }
-    .lh-category .lh-gauge__svg-wrapper {
-      --gauge-circle-size: var(--gauge-circle-size-big);
-    }
-
-    /* The plugin badge overlay */
-    .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-image: var(--plugin-icon-url);
-      background-repeat: no-repeat;
-      background-size: var(--plugin-icon-size);
-      background-position: 58% 50%;
-      content: "";
-      position: absolute;
-      right: -6px;
-      bottom: 0px;
-      display: block;
-      z-index: 100;
-      box-shadow: 0 0 4px rgba(0,0,0,.2);
-      border-radius: 25%;
-    }
-    .lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {
-      width: var(--plugin-badge-size-big);
-      height: var(--plugin-badge-size-big);
-    }
-
-    @keyframes load-gauge {
-      from { stroke-dasharray: 0 352; }
-    }
-
-    .lh-gauge__percentage {
-      width: 100%;
-      height: var(--inset-size);
-      position: absolute;
-      border-radius: inherit;
-      font-family: var(--monospace-font-family);
-      font-size: var(--score-number-font-size);
-      line-height: var(--score-number-font-size);
-      text-align: center;
-      top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 3);
-    }
-
-    .lh-category .lh-gauge__percentage {
-      --gauge-circle-size: var(--gauge-circle-size-big);
-      --score-number-font-size: var(--score-number-font-size-big);
-    }
-
-    .lh-gauge__wrapper {
-      position: relative;
-      display: flex;
-      align-items: center;
-      flex-direction: column;
-      text-decoration: none;
-
-      --circle-border-width: 8;
-      --transition-length: 1s;
-
-      /* Contain the layout style paint & layers during animation*/
-      contain: content;
-      will-change: opacity; /* Only using for layer promotion */
-    }
-
-    .lh-gauge__label {
-      font-size: var(--score-title-font-size);
-      line-height: var(--score-title-line-height);
-      margin-top: 10px;
-      text-align: center;
-      color: var(--body-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);
-      margin-top: 14px;
-    }
-
-  </style>
   <a href="#" class="lh-gauge__wrapper">
     <!-- Wrapper exists for the ::before plugin icon. Cannot create pseudo-elements on svgs. -->
     <div class="lh-gauge__svg-wrapper">
diff --git a/package.json b/package.json
index 52098cfe8..d18de01f1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "lighthouse",
-  "version": "4.3.0",
+  "version": "5.0.0",
   "description": "Lighthouse",
   "main": "./lighthouse-core/index.js",
   "bin": {
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 401a3ff9f..3ba37e774 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -3900,7 +3900,7 @@
             "warningHeader": "Warnings: "
         }
     },
-    "lighthouseVersion": "4.3.0",
+    "lighthouseVersion": "5.0.0",
     "requestedUrl": "http://localhost:10200/dobetterweb/dbw_tester.html",
     "runWarnings": [],
     "stackPacks": [
diff --git a/types/budget.d.ts b/types/budget.d.ts
index fa8632f1b..4c1d058b2 100644
--- a/types/budget.d.ts
+++ b/types/budget.d.ts
@@ -37,7 +37,7 @@ declare global {
       }
 
       /** Supported timing metrics. */
-      export type TimingMetric = 'first-contentful-paint' | 'first-cpu-idle' | 'interactive' | 'first-meaningful-paint' | 'estimated-input-latency';
+      export type TimingMetric = 'first-contentful-paint' | 'first-cpu-idle' | 'interactive' | 'first-meaningful-paint' | 'max-potential-fid';
 
       /** Supported values for the resourceType property of a ResourceBudget. */
       export type ResourceType = 'stylesheet' | 'image' | 'media' | 'font' | 'script' | 'document' | 'other' | 'total' | 'third-party';


Test patch:
diff --git a/lighthouse-cli/test/smokehouse/perf/perf-config.js b/lighthouse-cli/test/smokehouse/perf/perf-config.js
index 6b22341a9..f253a0502 100644
--- a/lighthouse-cli/test/smokehouse/perf/perf-config.js
+++ b/lighthouse-cli/test/smokehouse/perf/perf-config.js
@@ -41,7 +41,7 @@ const perfConfig = {
         {metric: 'first-cpu-idle', budget: 2000, tolerance: 100},
         {metric: 'interactive', budget: 2000, tolerance: 100},
         {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100},
-        {metric: 'estimated-input-latency', budget: 2000, tolerance: 100},
+        {metric: 'max-potential-fid', budget: 2000, tolerance: 100},
       ],
     }],
   },
diff --git a/lighthouse-cli/test/smokehouse/smokehouse.js b/lighthouse-cli/test/smokehouse/smokehouse.js
index 3373aa0c3..603af8bc3 100755
--- a/lighthouse-cli/test/smokehouse/smokehouse.js
+++ b/lighthouse-cli/test/smokehouse/smokehouse.js
@@ -49,15 +49,17 @@ function runLighthouse(url, configPath, isDebug) {
   isDebug = isDebug || Boolean(process.env.LH_SMOKE_DEBUG);
 
   const command = 'node';
-  const outputPath = `smokehouse-${Math.round(Math.random() * 100000)}.report.json`;
-  const artifactsDirectory = './.tmp/smokehouse-artifacts';
+  const randInt = Math.round(Math.random() * 100000);
+  const outputPath = `smokehouse-${randInt}.report.json`;
+  const artifactsDirectory = `./.tmp/smokehouse-artifacts-${randInt}`;
   const args = [
     'lighthouse-cli/index.js',
     url,
     `--config-path=${configPath}`,
     `--output-path=${outputPath}`,
     '--output=json',
-    `-GA=${artifactsDirectory}`,
+    `-G=${artifactsDirectory}`,
+    `-A=${artifactsDirectory}`,
     '--quiet',
     '--port=0',
   ];
@@ -114,8 +116,11 @@ function runLighthouse(url, configPath, isDebug) {
     }
   }
 
-  const artifacts = JSON.parse(
-    fs.readFileSync(`${artifactsDirectory}/artifacts.json`, 'utf8'));
+  // Artifacts are undefined if they weren't written to disk (e.g. if there was an error).
+  let artifacts;
+  try {
+    artifacts = JSON.parse(fs.readFileSync(`${artifactsDirectory}/artifacts.json`, 'utf8'));
+  } catch (e) {}
 
   return {
     errorCode,
diff --git a/lighthouse-core/test/config/budget-test.js b/lighthouse-core/test/config/budget-test.js
index 19de4fb00..20b4843e8 100644
--- a/lighthouse-core/test/config/budget-test.js
+++ b/lighthouse-core/test/config/budget-test.js
@@ -146,6 +146,18 @@ describe('Budget', () => {
       assert.throws(_ => Budget.initializeBudget(budget),
         /Resource Budget has unrecognized properties: \[browser\]/);
     });
+
+    it('throws when a duplicate resourceType is specified in resourceSizes', () => {
+      budget[1].resourceSizes.push({resourceType: 'script', budget: 100});
+      assert.throws(_ => Budget.initializeBudget(budget),
+        /budgets\[1\]\.resourceSizes has duplicate entry of type 'script'/);
+    });
+
+    it('throws when a duplicate resourceType is specified in resourceCounts', () => {
+      budget[0].resourceCounts.push({resourceType: 'third-party', budget: 100});
+      assert.throws(_ => Budget.initializeBudget(budget),
+        /budgets\[0\]\.resourceCounts has duplicate entry of type 'third-party'/);
+    });
   });
 
   describe('timing budget validation', () => {
@@ -172,5 +184,11 @@ describe('Budget', () => {
       assert.throws(_ => Budget.initializeBudget(budget),
         /Timing Budget has unrecognized properties: \[device, location\]/);
     });
+
+    it('throws when a duplicate metric type is specified in timings', () => {
+      budget[0].timings.push({metric: 'interactive', budget: 1000});
+      assert.throws(_ => Budget.initializeBudget(budget),
+        /budgets\[0\]\.timings has duplicate entry of type 'interactive'/);
+    });
   });
 });
diff --git a/lighthouse-core/test/report/html/renderer/dom-test.js b/lighthouse-core/test/report/html/renderer/dom-test.js
index ea91f810f..54c623dad 100644
--- a/lighthouse-core/test/report/html/renderer/dom-test.js
+++ b/lighthouse-core/test/report/html/renderer/dom-test.js
@@ -69,8 +69,8 @@ describe('DOM', () => {
     });
 
     it('does not inject duplicate styles', () => {
-      const clone = dom.cloneTemplate('#tmpl-lh-gauge', dom.document());
-      const clone2 = dom.cloneTemplate('#tmpl-lh-gauge', dom.document());
+      const clone = dom.cloneTemplate('#tmpl-lh-snippet', dom.document());
+      const clone2 = dom.cloneTemplate('#tmpl-lh-snippet', dom.document());
       assert.ok(clone.querySelector('style'));
       assert.ok(!clone2.querySelector('style'));
     });
diff --git a/lighthouse-core/test/report/html/renderer/psi-test.js b/lighthouse-core/test/report/html/renderer/psi-test.js
index 57823bcad..c9b0b346d 100644
--- a/lighthouse-core/test/report/html/renderer/psi-test.js
+++ b/lighthouse-core/test/report/html/renderer/psi-test.js
@@ -82,7 +82,6 @@ describe('DOM', () => {
         const result = prepareLabData(sampleResultsStr, document);
         assert.ok(result.scoreGaugeEl instanceof document.defaultView.Element);
         assert.equal(result.scoreGaugeEl.querySelector('.lh-gauge__wrapper').href, '');
-        assert.ok(result.scoreGaugeEl.outerHTML.includes('<style>'), 'score gauge comes with CSS');
         assert.ok(result.scoreGaugeEl.outerHTML.includes('<svg'), 'score gauge comes with SVG');
 
         assert.ok(result.perfCategoryEl instanceof document.defaultView.Element);
diff --git a/lighthouse-core/test/report/html/renderer/report-renderer-test.js b/lighthouse-core/test/report/html/renderer/report-renderer-test.js
index 81da4fe67..9e4836459 100644
--- a/lighthouse-core/test/report/html/renderer/report-renderer-test.js
+++ b/lighthouse-core/test/report/html/renderer/report-renderer-test.js
@@ -82,8 +82,6 @@ describe('ReportRenderer', () => {
       // 3 sets of gauges - one in sticky header, one in scores header, and one in each section.
       assert.equal(output.querySelectorAll('.lh-gauge__wrapper, .lh-gauge--pwa__wrapper').length,
           sampleResults.reportCategories.length * 3, 'renders category gauges');
-      // no fireworks
-      assert.ok(output.querySelector('.score100') === null, 'has no fireworks treatment');
     });
 
     it('renders additional reports by replacing the existing one', () => {
@@ -214,23 +212,6 @@ describe('ReportRenderer', () => {
     assert.equal(renderer._templateContext, otherDocument);
   });
 
-  it('should render an all 100 report with fireworks', () => {
-    const container = renderer._dom._document.body;
-
-    sampleResults.reportCategories.forEach(element => {
-      element.score = 1;
-    });
-
-    const output = renderer.renderReport(sampleResults, container);
-    // standard checks
-    assert.ok(output.querySelector('.lh-header-sticky'), 'has a header');
-    assert.ok(output.querySelector('.lh-report'), 'has report body');
-    assert.equal(output.querySelectorAll('.lh-gauge__wrapper, .lh-gauge--pwa__wrapper').length,
-        sampleResults.reportCategories.length * 3, 'renders category gauges');
-    // fireworks!
-    assert.ok(output.querySelector('.score100'), 'has fireworks treatment');
-  });
-
   it('should add LHR channel to doc link parameters', () => {
     const lhrChannel = sampleResults.configSettings.channel;
     // Make sure we have a channel in the LHR.
diff --git a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
index 4e83cd0cc..54a3d7852 100644
--- a/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
+++ b/lighthouse-core/test/report/html/renderer/report-ui-features-test.js
@@ -171,4 +171,22 @@ describe('ReportUIFeatures', () => {
       });
     });
   });
+
+  describe('fireworks', () => {
+    it('should render an non-all 100 report without fireworks', () => {
+      const lhr = JSON.parse(JSON.stringify(sampleResults));
+      lhr.categories.performance.score = 0.5;
+      const container = render(lhr);
+      assert.ok(container.querySelector('.score100') === null, 'has no fireworks treatment');
+    });
+
+    it('should render an all 100 report with fireworks', () => {
+      const lhr = JSON.parse(JSON.stringify(sampleResults));
+      Object.values(lhr.categories).forEach(element => {
+        element.score = 1;
+      });
+      const container = render(lhr);
+      assert.ok(container.querySelector('.score100'), 'has fireworks treatment');
+    });
+  });
 });
diff --git a/lighthouse-core/test/results/sample-config.js b/lighthouse-core/test/results/sample-config.js
index 9f8144dbb..8f63818ce 100644
--- a/lighthouse-core/test/results/sample-config.js
+++ b/lighthouse-core/test/results/sample-config.js
@@ -42,7 +42,7 @@ const budgetedConfig = {
         {metric: 'first-cpu-idle', budget: 2900, tolerance: 100},
         {metric: 'interactive', budget: 2900, tolerance: 100},
         {metric: 'first-meaningful-paint', budget: 2000, tolerance: 100},
-        {metric: 'estimated-input-latency', budget: 100, tolerance: 100},
+        {metric: 'max-potential-fid', budget: 100, tolerance: 100},
       ],
     }],
   },
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index 7fe39c6e7..89e0bd6c6 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -5,7 +5,7 @@
     "hostUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3358.0 Safari/537.36",
     "benchmarkIndex": 1000
   },
-  "lighthouseVersion": "4.3.0",
+  "lighthouseVersion": "5.0.0",
   "fetchTime": "2018-03-13T00:55:45.840Z",
   "requestedUrl": "http://localhost:10200/dobetterweb/dbw_tester.html",
   "finalUrl": "http://localhost:10200/dobetterweb/dbw_tester.html",
@@ -3290,7 +3290,7 @@
             "tolerance": 100
           },
           {
-            "metric": "estimated-input-latency",
+            "metric": "max-potential-fid",
             "budget": 100,
             "tolerance": 100
           }