Bug 1533832 - Add CFR Pin Tab, Discovery dark theme and bug fixes to Activity Stream r=k88hudson
authorEd Lee <edilee@mozilla.com>
Fri, 08 Mar 2019 21:37:36 +0000
changeset 524205 a014bada46195b51a7eea227deef890b198b41c2
parent 524204 e42dc1d5f378efabeeef7e7f4ed194bda4ba5d55
child 524206 5e103d74526dea1ec28d69859a157c8d4811444e
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersk88hudson
bugs1533832
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1533832 - Add CFR Pin Tab, Discovery dark theme and bug fixes to Activity Stream r=k88hudson Differential Revision: https://phabricator.services.mozilla.com/D22754
browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
browser/components/newtab/content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json
browser/components/newtab/content-src/components/Base/Base.jsx
browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
browser/components/newtab/content-src/components/DiscoveryStreamBase/_DiscoveryStreamBase.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/_DSLinkMenu.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/HorizontalRule/_HorizontalRule.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss
browser/components/newtab/content-src/components/DiscoveryStreamComponents/SectionTitle/_SectionTitle.scss
browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
browser/components/newtab/content-src/styles/_activity-stream.scss
browser/components/newtab/content-src/styles/_mixins.scss
browser/components/newtab/content-src/styles/_variables.scss
browser/components/newtab/css/activity-stream-linux.css
browser/components/newtab/css/activity-stream-mac.css
browser/components/newtab/css/activity-stream-windows.css
browser/components/newtab/data/content/activity-stream.bundle.js
browser/components/newtab/docs/v2-system-addon/data_dictionary.md
browser/components/newtab/docs/v2-system-addon/data_events.md
browser/components/newtab/lib/ASRouter.jsm
browser/components/newtab/lib/CFRMessageProvider.jsm
browser/components/newtab/lib/CFRPageActions.jsm
browser/components/newtab/lib/DownloadsManager.jsm
browser/components/newtab/lib/TelemetryFeed.jsm
browser/components/newtab/locales-src/id/strings.properties
browser/components/newtab/locales-src/nb-NO/strings.properties
browser/components/newtab/locales-src/nn-NO/strings.properties
browser/components/newtab/prerendered/locales/id/activity-stream-strings.js
browser/components/newtab/prerendered/locales/nb-NO/activity-stream-strings.js
browser/components/newtab/prerendered/locales/nn-NO/activity-stream-strings.js
browser/components/newtab/test/unit/asrouter/ASRouter.test.js
browser/components/newtab/test/unit/asrouter/CFRMessageProvider.test.js
browser/components/newtab/test/unit/asrouter/CFRPageActions.test.js
browser/components/newtab/test/unit/asrouter/constants.js
browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
browser/components/newtab/test/unit/unit-entry.js
--- a/browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
+++ b/browser/components/newtab/content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json
@@ -10,16 +10,20 @@
     },
     "linkUrl": {
       "description": "Target for links or buttons",
       "type": "string",
       "format": "uri"
     }
   },
   "properties": {
+    "category": {
+      "type": "string",
+      "description": "Attribute used for different groups of messages from the same provider"
+    },
     "bucket_id": {
       "type": "string",
       "description": "A bucket identifier for the addon. This is used in order to anonymize telemetry for history-sensitive targeting."
     },
     "notification_text": {
       "description": "The text in the small blue chicklet that appears in the URL bar. This can be a reference to a localized string in Firefox or just a plain string.",
       "oneOf": [
         {
@@ -161,16 +165,37 @@
               "type": "string",
               "description": "Id of string to localized addon description"
             }
           },
           "required": ["string_id"]
         }
       ]
     },
+    "descriptionDetails": {
+      "description": "Additional information and steps on how to use",
+      "type": "object",
+      "properties": {
+        "steps": {
+          "description": "Array of messages or string_ids",
+          "type": "array",
+          "items": {
+            "type": "object",
+            "properties": {
+              "string_id": {
+                "type": "string",
+                "description": "Id of string to localized addon description"
+              }
+            },
+            "required": ["string_id"]
+          }
+        }
+      },
+      "required": ["steps"]
+    },
     "buttons": {
       "description": "The label and functionality for the buttons in the pop-over.",
       "type": "object",
       "properties": {
         "primary": {
           "type": "object",
           "properties": {
             "label": {
@@ -294,10 +319,10 @@
               }
             }
           }
         }
       }
     }
   },
   "additionalProperties": false,
-  "required": ["bucket_id", "notification_text", "heading_text", "text", "buttons"]
+  "required": ["category", "bucket_id", "notification_text", "heading_text", "text", "buttons"]
 }
--- a/browser/components/newtab/content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json
+++ b/browser/components/newtab/content-src/asrouter/templates/EOYSnippet/EOYSnippet.schema.json
@@ -71,16 +71,22 @@
       "type": "string",
       "description": "Default donation_amount_second. Donation amount button that's selected by default.",
       "default": "donation_amount_second"
     },
     "icon": {
       "type": "string",
       "description": "Snippet icon. 64x64px. SVG or PNG preferred."
     },
+    "title": {
+      "allOf": [
+        {"$ref": "#/definitions/plainText"},
+        {"description": "Snippet title displayed before snippet text"}
+      ]
+    },
     "title_icon": {
       "type": "string",
       "description": "Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."
     },
     "button_label": {
       "allOf": [
         {"$ref": "#/definitions/plainText"},
         {"description": "Text for a button next to main snippet text that links to button_url. Requires button_url."}
--- a/browser/components/newtab/content-src/components/Base/Base.jsx
+++ b/browser/components/newtab/content-src/components/Base/Base.jsx
@@ -132,40 +132,27 @@ export class BaseContent extends React.P
     }
   }
 
   openPreferences() {
     this.props.dispatch(ac.OnlyToMain({type: at.SETTINGS_OPEN}));
     this.props.dispatch(ac.UserEvent({event: "OPEN_NEWTAB_PREFS"}));
   }
 
-  disableDarkTheme() {
-    // Dark themes are not supported in discovery stream view
-    // Add force-light-theme class to body tag to disable dark mode. See Bug 1519764
-    const bodyClassNames = global.document.body.classList;
-    if (!bodyClassNames.contains("force-light-theme")) {
-      bodyClassNames.add("force-light-theme");
-    }
-  }
-
   render() {
     const {props} = this;
     const {App} = props;
     const {initialized} = App;
     const prefs = props.Prefs.values;
 
     const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]);
     const noSectionsEnabled = !prefs["feeds.topsites"] && props.Sections.filter(section => section.enabled).length === 0;
     const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
     const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
 
-    if (isDiscoveryStream) {
-      this.disableDarkTheme();
-    }
-
     const outerClassName = [
       "outer-wrapper",
       isDiscoveryStream && "ds-outer-wrapper-search-alignment",
       isDiscoveryStream && "ds-outer-wrapper-breakpoint-override",
       shouldBeFixedToTop && "fixed-to-top",
       prefs.showSearch && this.state.fixedSearch && !noSectionsEnabled && "fixed-search",
       prefs.showSearch && noSectionsEnabled && "only-search",
     ].filter(v => v).join(" ");
--- a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
+++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
@@ -1,22 +1,30 @@
 import React from "react";
 
 export class ContextMenu extends React.PureComponent {
   constructor(props) {
     super(props);
     this.hideContext = this.hideContext.bind(this);
+    this.onShow = this.onShow.bind(this);
     this.onClick = this.onClick.bind(this);
   }
 
   hideContext() {
     this.props.onUpdate(false);
   }
 
+  onShow() {
+    if (this.props.onShow) {
+      this.props.onShow();
+    }
+  }
+
   componentDidMount() {
+    this.onShow();
     setTimeout(() => {
       global.addEventListener("click", this.hideContext);
     }, 0);
   }
 
   componentWillUnmount() {
     global.removeEventListener("click", this.hideContext);
   }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamBase/_DiscoveryStreamBase.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamBase/_DiscoveryStreamBase.scss
@@ -20,16 +20,20 @@
 
   .ds-column-grid {
     display: grid;
     grid-row-gap: var(--gridRowGap);
   }
 }
 
 .ds-header {
+  @include dark-theme-only {
+    color: $grey-30;
+  }
+
   color: $grey-50;
   font-size: 13px;
   font-weight: 600;
   line-height: 20px;
   margin: 8px 0;
 
   .icon {
     fill: var(--newtab-text-secondary-color);
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss
@@ -2,26 +2,39 @@
 $col4-header-font-size: 14;
 
 .ds-card-grid {
   display: grid;
   grid-gap: 24px;
   margin: 16px 0;
 
   .ds-card {
+    @include dark-theme-only {
+      background: none;
+    }
+
     background: $white;
     border-radius: 4px;
   }
 
   &.ds-card-grid-border {
     .ds-card {
-      box-shadow: var(--newtab-card-shadow);
+      @include dark-theme-only {
+        box-shadow: 0 1px 4px $shadow-10;
+        background: $grey-70;
+      }
+
+      box-shadow: 0 1px 4px 0 $grey-90-10;
 
       &:hover {
-        box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+        @include dark-theme-only {
+          box-shadow: 0 0 0 5px $grey-60;
+        }
+
+        box-shadow: 0 0 0 5px $grey-30;
         transition: box-shadow 150ms;
         outline: none;
       }
 
       .img-wrapper .img {
         border-radius: 4px 4px 0 0;
       }
     }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -1,9 +1,10 @@
 import {actionCreators as ac} from "common/Actions.jsm";
+import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
 export class DSCard extends React.PureComponent {
   constructor(props) {
     super(props);
 
@@ -23,40 +24,50 @@ export class DSCard extends React.PureCo
         click: 0,
         tiles: [{id: this.props.id, pos: this.props.pos}],
       }));
     }
   }
 
   render() {
     return (
-      <SafeAnchor
-        className="ds-card"
-        dispatch={this.props.dispatch}
-        onLinkClick={this.onLinkClick}
-        url={this.props.url}>
-        <div className="img-wrapper">
-          <div className="img" style={{backgroundImage: `url(${this.props.image_src}`}} />
-        </div>
-        <div className="meta">
-          <div className="info-wrap">
-            <header className="title">{this.props.title}</header>
-            {this.props.excerpt && <p className="excerpt">{this.props.excerpt}</p>}
+      <div className="ds-card">
+        <SafeAnchor
+          className="ds-card-link"
+          dispatch={this.props.dispatch}
+          onLinkClick={this.onLinkClick}
+          url={this.props.url}>
+          <div className="img-wrapper">
+            <div className="img" style={{backgroundImage: `url(${this.props.image_src}`}} />
           </div>
-          <p>
-            {this.props.context && (
-              <span>
-                <span className="context">{this.props.context}</span>
-                <br />
-              </span>
-            )}
-            <span className="source">{this.props.source}</span>
-          </p>
-        </div>
-        <ImpressionStats
-          campaignId={this.props.campaignId}
-          rows={[{id: this.props.id, pos: this.props.pos}]}
+          <div className="meta">
+            <div className="info-wrap">
+              <header className="title">{this.props.title}</header>
+              {this.props.excerpt && <p className="excerpt">{this.props.excerpt}</p>}
+            </div>
+            <p>
+              {this.props.context && (
+                <span>
+                  <span className="context">{this.props.context}</span>
+                  <br />
+                </span>
+              )}
+              <span className="source">{this.props.source}</span>
+            </p>
+          </div>
+          <ImpressionStats
+            campaignId={this.props.campaignId}
+            rows={[{id: this.props.id, pos: this.props.pos}]}
+            dispatch={this.props.dispatch}
+            source={this.props.type} />
+        </SafeAnchor>
+        <DSLinkMenu
+          index={this.props.index}
           dispatch={this.props.dispatch}
-          source={this.props.type} />
-      </SafeAnchor>
+          intl={this.props.intl}
+          url={this.props.url}
+          title={this.props.title}
+          source={this.props.source}
+          type={this.props.type} />
+      </div>
     );
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -6,16 +6,20 @@
 
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative;
 
   &:hover {
     header {
+      @include dark-theme-only {
+        color: $blue-40;
+      }
+
       color: $blue-60;
     }
   }
 
   &:active {
     header {
       color: $blue-70;
     }
@@ -26,16 +30,23 @@
   }
 
   .img {
     @include image-as-background;
     height: 0;
     padding-top: 50%; // 2:1 aspect ratio
   }
 
+  .ds-card-link {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    height: 100%;
+  }
+
   .meta {
     display: flex;
     flex-direction: column;
     flex-grow: 1;
     padding: 16px;
 
     .info-wrap {
       flex-grow: 1;
@@ -49,27 +60,38 @@
 
     .excerpt {
       // show only 3 lines of copy
       @include limit-visibile-lines(3, $excerpt-line-height, $excerpt-font-size);
     }
 
     .context,
     .source {
+      @include dark-theme-only {
+        color: $teal-10;
+      }
+
       font-size: 13px;
       color: $teal-80;
     }
   }
 
   header {
+    @include dark-theme-only {
+      color: $grey-10;
+    }
+
     line-height: $header-line-height * 1px;
     font-size: $header-font-size * 1px;
     color: $grey-90;
-
   }
 
   p {
+    @include dark-theme-only {
+      color: $grey-30;
+    }
+
     font-size: $excerpt-font-size * 1px;
     line-height: $excerpt-line-height * 1px;
     color: $grey-50;
     margin: 8px 0 0;
   }
 }
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
@@ -0,0 +1,77 @@
+import {FormattedMessage, injectIntl} from "react-intl";
+import {LinkMenu} from "content-src/components/LinkMenu/LinkMenu";
+import React from "react";
+
+export class _DSLinkMenu extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = {
+      activeCard: null,
+      showContextMenu: false,
+    };
+    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
+    this.onMenuUpdate = this.onMenuUpdate.bind(this);
+    this.onMenuShow = this.onMenuShow.bind(this);
+    this.contextMenuButtonRef = React.createRef();
+  }
+
+  onMenuButtonClick(event) {
+    event.preventDefault();
+    this.setState({
+      activeCard: this.props.index,
+      showContextMenu: true,
+    });
+  }
+
+  onMenuUpdate(showContextMenu) {
+    if (!showContextMenu) {
+      const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
+      dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
+    }
+    this.setState({showContextMenu});
+  }
+
+  onMenuShow() {
+    const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
+    if (window.scrollMaxX > 0) {
+      dsLinkMenuHostDiv.parentElement.classList.add("last-item");
+    }
+    dsLinkMenuHostDiv.parentElement.classList.add("active");
+  }
+
+  render() {
+    const {index, dispatch} = this.props;
+    const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
+    const TOP_STORIES_SOURCE = "TOP_STORIES";
+    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow"];
+    const title = this.props.title || this.props.source;
+
+    return (<div>
+      <button ref={this.contextMenuButtonRef}
+              className="context-menu-button icon"
+              title={this.props.intl.formatMessage({id: "context_menu_title"})}
+              onClick={this.onMenuButtonClick}>
+        <span className="sr-only">
+          <FormattedMessage id="context_menu_button_sr" values={{title}} />
+        </span>
+      </button>
+      {isContextMenuOpen &&
+        <LinkMenu
+          dispatch={dispatch}
+          index={index}
+          source={TOP_STORIES_SOURCE}
+          onUpdate={this.onMenuUpdate}
+          onShow={this.onMenuShow}
+          options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
+          site={{
+            referrer: "https://getpocket.com/recommendations",
+            title: this.props.title,
+            type: this.props.type,
+            url: this.props.url,
+          }} />
+      }
+    </div>);
+  }
+}
+
+export const DSLinkMenu = injectIntl(_DSLinkMenu);
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/_DSLinkMenu.scss
@@ -0,0 +1,32 @@
+.ds-hero-item,
+.ds-list-item,
+.ds-card {
+  @include context-menu-button;
+
+  .context-menu {
+    opacity: 0;
+  }
+
+  &.active {
+    .context-menu {
+      opacity: 1;
+    }
+  }
+
+  &.last-item {
+    @include context-menu-open-left;
+
+    .context-menu {
+      opacity: 1;
+    }
+  }
+
+  &:-moz-any(:hover, :focus, .active) {
+    @include context-menu-button-hover;
+    outline: none;
+
+    &.ds-card-grid-border {
+      @include fade-in-card;
+    }
+  }
+}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss
@@ -1,27 +1,35 @@
 .ds-message {
   margin: 8px 0 0;
 
   .title {
     display: flex;
     align-items: center;
 
     .glyph {
+      @include dark-theme-only {
+        fill: $grey-30;
+      }
+
       width: 16px;
       height: 16px;
       margin: 0 6px 0 0;
       -moz-context-properties: fill;
-      fill: var(--newtab-icon-secondary-color);
+      fill: $grey-50;
       background-position: center center;
       background-size: 16px;
       background-repeat: no-repeat;
     }
 
     .title-text {
+      @include dark-theme-only {
+        color: $grey-30;
+      }
+
       line-height: 20px;
       font-size: 13px;
       color: $grey-50;
       font-weight: 600;
       padding-right: 12px;
     }
 
     .link {
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
@@ -1,10 +1,11 @@
 import {actionCreators as ac} from "common/Actions.jsm";
 import {DSCard} from "../DSCard/DSCard.jsx";
+import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import {List} from "../List/List.jsx";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
 export class Hero extends React.PureComponent {
   constructor(props) {
     super(props);
@@ -64,41 +65,51 @@ export class Hero extends React.PureComp
         items={this.props.items - 1}
         type={`Hero`} />
     );
 
     return (
       <div>
         <div className="ds-header">{this.props.title}</div>
         <div className={`ds-hero ds-hero-${this.props.border}`}>
-          <SafeAnchor
-            className="wrapper"
-            dispatch={this.props.dispatch}
-            onLinkClick={this.onLinkClick}
-            url={heroRec.url}>
-            <div className="img-wrapper">
-              <div className="img" style={{backgroundImage: `url(${heroRec.image_src})`}} />
-            </div>
-            <div className="meta">
-              <div className="header-and-excerpt">
-                <header>{heroRec.title}</header>
-                <p className="excerpt">{heroRec.excerpt}</p>
+          <div className="ds-hero-item">
+            <SafeAnchor
+              className="wrapper"
+              dispatch={this.props.dispatch}
+              onLinkClick={this.onLinkClick}
+              url={heroRec.url}>
+              <div className="img-wrapper">
+                <div className="img" style={{backgroundImage: `url(${heroRec.image_src})`}} />
               </div>
-              {heroRec.context ? (
-                <p className="context">{heroRec.context}</p>
-              ) : (
-                <p className="source">{heroRec.domain}</p>
-              )}
-            </div>
-            <ImpressionStats
-              campaignId={heroRec.campaignId}
-              rows={[{id: heroRec.id, pos: heroRec.pos}]}
+              <div className="meta">
+                <div className="header-and-excerpt">
+                  <header>{heroRec.title}</header>
+                  <p className="excerpt">{heroRec.excerpt}</p>
+                </div>
+                {heroRec.context ? (
+                  <p className="context">{heroRec.context}</p>
+                ) : (
+                  <p className="source">{heroRec.domain}</p>
+                )}
+              </div>
+              <ImpressionStats
+                campaignId={heroRec.campaignId}
+                rows={[{id: heroRec.id, pos: heroRec.pos}]}
+                dispatch={this.props.dispatch}
+                source={this.props.type} />
+            </SafeAnchor>
+            <DSLinkMenu
+              index={this.props.index}
               dispatch={this.props.dispatch}
-              source={this.props.type} />
-          </SafeAnchor>
+              intl={this.props.intl}
+              url={heroRec.url}
+              title={heroRec.title}
+              source={heroRec.domain}
+              type={this.props.type} />
+          </div>
           <div className={`${this.props.subComponentType}`}>
             { this.props.subComponentType === `cards` ? cards : list }
           </div>
         </div>
       </div>
     );
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
@@ -49,36 +49,53 @@
       margin: 0 0 12px;
     }
   }
 
   .img-wrapper {
     margin: 0 0 12px;
   }
 
+  .ds-hero-item {
+    position: relative;
+  }
+
   // "1/3 width layout" (aka "Mobile First")
   .wrapper {
+    @include ds-border-top;
+    @include ds-border-bottom;
+    @include dark-theme-only {
+      color: $grey-30;
+    }
+
     color: $grey-50;
     display: block;
     margin: 12px 0 16px;
     padding: 16px 0;
-    border-top: $border-secondary;
-    border-bottom: $border-secondary;
+    height: 100%;
 
     @at-root .ds-hero-no-border .wrapper {
       border-top: 0;
       border-bottom: 0;
       padding: 0 0 8px;
     }
 
     &:hover .meta header {
+      @include dark-theme-only {
+        color: $blue-40;
+      }
+
       color: $blue-60;
     }
 
     &:active .meta header {
+      @include dark-theme-only {
+        color: $blue-40;
+      }
+
       color: $blue-70;
     }
 
     .img-wrapper {
       width: 100%;
     }
 
     .img {
@@ -87,26 +104,38 @@
     }
 
     .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between;
 
       header {
+        @include dark-theme-only {
+          color: $white;
+        }
+
         @include limit-visibile-lines(4, 28, 22);
         color: $grey-90;
         margin-bottom: 8px;
       }
 
       .context {
+        @include dark-theme-only {
+          color: $teal-10;
+        }
+
         color: $teal-70;
       }
 
       .source {
+        @include dark-theme-only {
+          color: $teal-10;
+        }
+
         font-size: 13px;
         color: $teal-80;
         margin-bottom: 0;
         overflow-x: hidden;
         text-overflow: ellipsis;
       }
     }
   }
@@ -151,21 +180,21 @@
   .ds-column-10 &,
   .ds-column-11 &,
   .ds-column-12 & {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px;
 
     &.ds-hero-border {
-      border-top: $border-secondary;
+      @include ds-border-top;
       padding: 20px 0;
 
       .ds-card:nth-child(-n+2) {
-        border-bottom: $border-secondary;
+        @include ds-border-bottom;
         margin-bottom: 20px;
       }
     }
 
     .wrapper {
       border-top: 0;
       border-bottom: 0;
       margin: 0;
@@ -198,14 +227,36 @@
       }
     }
 
     .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px;
 
-      .title {
-        @include limit-visibile-lines(3, 20, 14);
+      .ds-card {
+        &:hover {
+          @include dark-theme-only {
+            background: none;
+
+            .title {
+              color: $blue-40;
+            }
+          }
+        }
+
+        &:active .title {
+          @include dark-theme-only {
+            color: $blue-50;
+          }
+        }
+
+        .title {
+          @include dark-theme-only {
+            color: $white;
+          }
+
+          @include limit-visibile-lines(3, 20, 14);
+        }
       }
     }
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/HorizontalRule/_HorizontalRule.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/HorizontalRule/_HorizontalRule.scss
@@ -1,5 +1,7 @@
 .ds-hr {
-  border: 0;
+  @include ds-border-top {
+    border: 0;
+  };
+
   height: 0;
-  border-top: $border-secondary;
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
@@ -1,10 +1,11 @@
 import {actionCreators as ac} from "common/Actions.jsm";
 import {connect} from "react-redux";
+import {DSLinkMenu} from "../DSLinkMenu/DSLinkMenu";
 import {ImpressionStats} from "../../DiscoveryStreamImpressionStats/ImpressionStats";
 import React from "react";
 import {SafeAnchor} from "../SafeAnchor/SafeAnchor";
 
 /**
  * @note exported for testing only
  */
 export class ListItem extends React.PureComponent {
@@ -56,16 +57,24 @@ export class ListItem extends React.Pure
           </div>
           <div className="ds-list-image" style={{backgroundImage: `url(${this.props.image_src})`}} />
           <ImpressionStats
             campaignId={this.props.campaignId}
             rows={[{id: this.props.id, pos: this.props.pos}]}
             dispatch={this.props.dispatch}
             source={this.props.type} />
         </SafeAnchor>
+        <DSLinkMenu
+          index={this.props.index}
+          dispatch={this.props.dispatch}
+          intl={this.props.intl}
+          url={this.props.url}
+          title={this.props.title}
+          source={this.props.source}
+          type={this.props.type} />
       </li>
     );
   }
 }
 
 /**
  * @note exported for testing only
  */
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
@@ -5,17 +5,17 @@
 $item-line-height: 20;
 
 // XXX this is gross, and attaches the bottom-border to the item above.
 // Ideally, we'd attach the top-border to the item that needs it.
 // Unfortunately the border needs to go _above_ the row gap as currently
 // set up, which means that some refactoring will be required to do this.
 @mixin bottom-border-except-last-grid-row($columns) {
   .ds-list-item:not(:nth-last-child(-n+#{$columns})) {
-    border-bottom: $border-secondary;
+    @include ds-border-bottom;
     margin-bottom: -1px;  // cancel out the pixel we used for the border
     padding-bottom: $bordered-spacing;
   }
 }
 
 @mixin set-item-sizes($font-size, $line-height, $image-size) {
   .ds-list-item {
     // XXX see if we really want absolute units, maybe hoist somewhere central?
@@ -69,17 +69,20 @@
 
   &:not(.ds-list-images) {
     .ds-list-image {
       display: none;
     }
   }
 
   a {
-    // XXX note that this only looks right in the light theme
+    @include dark-theme-only {
+      color: $grey-10;
+    }
+
     color: $grey-90;
   }
 }
 
 .ds-list-numbers {
   $counter-whitespace: ($item-line-height - $item-font-size) * 1px;
   $counter-size: 32px;
   $counter-padded-size: $counter-size + $counter-whitespace * 1.5;
@@ -87,42 +90,54 @@
   .ds-list-item {
     counter-increment: list;
   }
 
   .ds-list-item-link {
     padding-inline-start: $counter-padded-size;
 
     &::before {
-      background-color: var(--newtab-link-secondary-color);
+      @include dark-theme-only {
+        background-color: $teal-70;
+      }
+
+      background-color: $pocket-teal;
       border-radius: $counter-size;
       color: $white;
       content: counter(list);
       font-size: 17px;
       height: $counter-size;
       line-height: $counter-size;
       margin-inline-start: -$counter-padded-size;
       margin-top: $counter-whitespace / 2;
       position: absolute;
       text-align: center;
       width: $counter-size;
     }
 
     &:hover::before {
-      background-color: var(--newtab-link-primary-color);
+      @include dark-theme-only {
+        background-color: $blue-40;
+      }
+
+      background-color: $blue-40;
     }
 
     &:active::before {
+      @include dark-theme-only {
+        background-color: $blue-60;
+      }
+
       background-color: $blue-70;
     }
   }
 }
 
 .ds-list-borders {
-  border-top: $border-secondary;
+  @include ds-border-top;
   grid-row-gap: $bordered-spacing;
   padding-top: $bordered-spacing;
 
   &.ds-list-full-width,
   .ds-column-1 &,
   .ds-column-2 &,
   .ds-column-3 &,
   .ds-column-4 & {
@@ -163,29 +178,33 @@
 
     display: flex;
     justify-content: space-between;
     height: 100%;
   }
 
   .ds-list-item-excerpt {
     @include limit-visibile-lines(2, $item-line-height, $item-font-size);
-    color: var(--newtab-text-secondary-color);
+    color: $grey-10-80;
     margin: 4px 0 8px;
   }
 
   p {
     font-size: $item-font-size * 1px;
     line-height: $item-line-height * 1px;
     margin: 0;
   }
 
   .ds-list-item-info,
   .ds-list-item-context {
     @include limit-visibile-lines(1, $item-line-height, $item-font-size);
+    @include dark-theme-only {
+      color: $teal-10;
+    }
+
     color: $teal-80;
     font-size: 13px;
     text-overflow: ellipsis;
   }
 
   .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px;
@@ -201,17 +220,17 @@
     @include image-as-background;
     height: $item-image-size;
     margin-inline-start: $item-font-size * 1px;
     min-height: $item-image-size;
   }
 
   &:hover {
     .ds-list-item-title {
-      color: var(--newtab-link-primary-color);
+      color: $blue-40;
     }
   }
 
   &:active {
     .ds-list-item-title {
       color: $blue-70;
     }
   }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss
@@ -28,17 +28,17 @@
 
     &:last-child::after {
       content: none;
     }
 
     a {
       &:hover {
         // text-decoration: underline; didn't quite match comps.
-        border-bottom: 1px solid var(--newtab-link-primary-color);
+        border-bottom: 1px solid $blue-40;
 
         &:active {
           border-bottom: 1px solid $blue-70;
         }
       }
 
       &:active {
         color: $blue-70;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SectionTitle/_SectionTitle.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SectionTitle/_SectionTitle.scss
@@ -1,18 +1,26 @@
 .ds-section-title {
   text-align: center;
   margin-top: 24px;
 
   .title {
+    @include dark-theme-only {
+      color: $white;
+    }
+
     line-height: 48px;
     font-size: 36px;
     font-weight: 300;
     color: $grey-90;
   }
 
   .subtitle {
+    @include dark-theme-only {
+      color: $grey-30;
+    }
+
     line-height: 24px;
     font-size: 14px;
     color: $grey-50;
     margin-top: 4px;
   }
 }
--- a/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
+++ b/browser/components/newtab/content-src/components/LinkMenu/LinkMenu.jsx
@@ -43,14 +43,15 @@ export class _LinkMenu extends React.Pur
     options[0].first = true;
     options[options.length - 1].last = true;
     return options;
   }
 
   render() {
     return (<ContextMenu
       onUpdate={this.props.onUpdate}
+      onShow={this.props.onShow}
       options={this.getOptions()} />);
   }
 }
 
 const getState = state => ({isPrivateBrowsingEnabled: state.Prefs.values.isPrivateBrowsingEnabled, platform: state.Prefs.values.platform});
 export const LinkMenu = connect(getState)(injectIntl(_LinkMenu));
--- a/browser/components/newtab/content-src/styles/_activity-stream.scss
+++ b/browser/components/newtab/content-src/styles/_activity-stream.scss
@@ -150,16 +150,17 @@ input {
 // Discovery Stream Components
 @import '../components/DiscoveryStreamComponents/CardGrid/CardGrid';
 @import '../components/DiscoveryStreamComponents/Hero/Hero';
 @import '../components/DiscoveryStreamComponents/HorizontalRule/HorizontalRule';
 @import '../components/DiscoveryStreamComponents/List/List';
 @import '../components/DiscoveryStreamComponents/Navigation/Navigation';
 @import '../components/DiscoveryStreamComponents/SectionTitle/SectionTitle';
 @import '../components/DiscoveryStreamComponents/TopSites/TopSites';
+@import '../components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu';
 @import '../components/DiscoveryStreamComponents/DSCard/DSCard';
 @import '../components/DiscoveryStreamComponents/DSMessage/DSMessage';
 @import '../components/DiscoveryStreamImpressionStats/ImpressionStats';
 
 // AS Router
 @import '../asrouter/components/Button/Button';
 @import '../asrouter/components/SnippetBase/SnippetBase';
 @import '../asrouter/components/ModalOverlay/ModalOverlay';
--- a/browser/components/newtab/content-src/styles/_mixins.scss
+++ b/browser/components/newtab/content-src/styles/_mixins.scss
@@ -10,8 +10,34 @@
 
 // Note: lineHeight and fontSize should be unitless but can be derived from pixel values
 @mixin limit-visibile-lines($line-count, $line-height, $font-size) {
   font-size: $font-size * 1px;
   line-height: $line-height * 1px;
   max-height: 1em * $line-count * $line-height / $font-size;
   overflow: hidden;
 }
+
+@mixin dark-theme-only {
+  [lwt-newtab-brighttext]:not(.force-light-theme) & {
+    @content;
+  }
+}
+
+@mixin ds-border-top {
+  @content;
+
+  @include dark-theme-only {
+    border-top: 1px solid $grey-60;
+  }
+
+  border-top: 1px solid $grey-30;
+}
+
+@mixin ds-border-bottom {
+  @content;
+
+  @include dark-theme-only {
+    border-bottom: 1px solid $grey-60;
+  }
+
+  border-bottom: 1px solid $grey-30;
+}
--- a/browser/components/newtab/content-src/styles/_variables.scss
+++ b/browser/components/newtab/content-src/styles/_variables.scss
@@ -8,16 +8,18 @@
 $grey-20: #EDEDF0;
 $grey-30: #D7D7DB;
 $grey-40: #B1B1B3;
 $grey-50: #737373;
 $grey-60: #4A4A4F;
 $grey-70: #38383D;
 $grey-80: #2A2A2E;
 $grey-90: #0C0C0D;
+$teal-10: #A7FFFE;
+$teal-60: #00C8D7;
 $teal-70: #008EA4;
 $teal-80: #005A71;
 $red-60: #D70022;
 $yellow-50: #FFE900;
 
 // Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity
 $grey-10-10: rgba($grey-10, 0.1);
 $grey-10-20: rgba($grey-10, 0.2);
@@ -52,16 +54,17 @@
 $black-25: rgba($black, 0.25);
 $black-30: rgba($black, 0.3);
 
 // Other colors
 $white: #FFF;
 $white-10: rgba($white, 0.1);
 $pocket-teal: #50BCB6;
 $pocket-red: #EF4056;
+$shadow-10: rgba(12, 12, 13, 0.1);
 $bookmark-icon-fill: #0A84FF;
 $download-icon-fill: #12BC00;
 $pocket-icon-fill: #D70022;
 $email-input-focus: rgba($blue-50, 0.3);
 $email-input-invalid: rgba($red-60, 0.3);
 $aw-extra-blue-1: #004EC2;
 $aw-extra-blue-2: #0080FF;
 $aw-extra-blue-3: #00C7FF;
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -1837,16 +1837,18 @@ main {
     grid-row-gap: var(--gridRowGap); }
 
 .ds-header {
   color: #737373;
   font-size: 13px;
   font-weight: 600;
   line-height: 20px;
   margin: 8px 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-header {
+    color: #D7D7DB; }
   .ds-header .icon {
     fill: var(--newtab-text-secondary-color); }
 
 .ds-message-container {
   display: none;
   color: #737373;
   font-size: 13px;
   justify-content: center;
@@ -1880,22 +1882,29 @@ main {
 
 .ds-card-grid {
   display: grid;
   grid-gap: 24px;
   margin: 16px 0; }
   .ds-card-grid .ds-card {
     background: #FFF;
     border-radius: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid .ds-card {
+      background: none; }
   .ds-card-grid.ds-card-grid-border .ds-card {
-    box-shadow: var(--newtab-card-shadow); }
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card {
+      box-shadow: 0 1px 4px rgba(12, 12, 13, 0.1);
+      background: #38383D; }
     .ds-card-grid.ds-card-grid-border .ds-card:hover {
-      box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+      box-shadow: 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms;
       outline: none; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card:hover {
+        box-shadow: 0 0 0 5px #4A4A4F; }
     .ds-card-grid.ds-card-grid-border .ds-card .img-wrapper .img {
       border-radius: 4px 4px 0 0; }
   .ds-card-grid.ds-card-grid-no-border .ds-card {
     background: none; }
     .ds-card-grid.ds-card-grid-no-border .ds-card .meta {
       padding: 16px 0; }
   .ds-column-5 .ds-card-grid,
   .ds-column-6 .ds-card-grid,
@@ -1960,55 +1969,74 @@ main {
       box-shadow: none;
       border-radius: 0; }
     .ds-hero .ds-card .meta {
       padding: 0; }
     .ds-hero .ds-card .img-wrapper {
       margin: 0 0 12px; }
   .ds-hero .img-wrapper {
     margin: 0 0 12px; }
+  .ds-hero .ds-hero-item {
+    position: relative; }
   .ds-hero .wrapper {
+    border-top: 1px solid #D7D7DB;
+    border-bottom: 1px solid #D7D7DB;
     color: #737373;
     display: block;
     margin: 12px 0 16px;
     padding: 16px 0;
-    border-top: 1px solid var(--newtab-border-secondary-color);
-    border-bottom: 1px solid var(--newtab-border-secondary-color); }
+    height: 100%; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-top: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-bottom: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      color: #D7D7DB; }
     .ds-hero-no-border .wrapper {
       border-top: 0;
       border-bottom: 0;
       padding: 0 0 8px; }
     .ds-hero .wrapper:hover .meta header {
       color: #0060DF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:hover .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper:active .meta header {
       color: #003EAA; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:active .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper .img-wrapper {
       width: 100%; }
     .ds-hero .wrapper .img {
       height: 0;
       padding-top: 50%; }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 8px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta header {
+          color: #FFF; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .context {
+          color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #005A71;
         margin-bottom: 0;
         overflow-x: hidden;
         text-overflow: ellipsis; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .source {
+          color: #A7FFFE; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-5 .ds-hero .wrapper .img-wrapper,
@@ -2044,24 +2072,34 @@ main {
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
     .ds-column-10 .ds-hero.ds-hero-border,
     .ds-column-11 .ds-hero.ds-hero-border,
     .ds-column-12 .ds-hero.ds-hero-border {
-      border-top: 1px solid var(--newtab-border-secondary-color);
+      border-top: 1px solid #D7D7DB;
       padding: 20px 0; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero.ds-hero-border {
+        border-top: 1px solid #4A4A4F; }
       .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
-        border-bottom: 1px solid var(--newtab-border-secondary-color);
+        border-bottom: 1px solid #D7D7DB;
         margin-bottom: 20px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
+          border-bottom: 1px solid #4A4A4F; }
     .ds-column-9 .ds-hero .wrapper,
     .ds-column-10 .ds-hero .wrapper,
     .ds-column-11 .ds-hero .wrapper,
     .ds-column-12 .ds-hero .wrapper {
       border-top: 0;
       border-bottom: 0;
       margin: 0;
       padding: 0 0 20px;
@@ -2101,29 +2139,51 @@ main {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px; }
-      .ds-column-9 .ds-hero .cards .title,
-      .ds-column-10 .ds-hero .cards .title,
-      .ds-column-11 .ds-hero .cards .title,
-      .ds-column-12 .ds-hero .cards .title {
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:hover {
+        background: none; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card:hover .title {
+          color: #45A1FF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:active .title {
+        color: #0A84FF; }
+      .ds-column-9 .ds-hero .cards .ds-card .title,
+      .ds-column-10 .ds-hero .cards .ds-card .title,
+      .ds-column-11 .ds-hero .cards .ds-card .title,
+      .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
         line-height: 20px;
         max-height: 4.28571em;
         overflow: hidden; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card .title {
+          color: #FFF; }
 
 .ds-hr {
   border: 0;
-  height: 0;
-  border-top: 1px solid var(--newtab-border-secondary-color); }
+  border-top: 1px solid #D7D7DB;
+  height: 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hr {
+    border-top: 1px solid #4A4A4F; }
 
 .ds-list {
   display: grid;
   grid-row-gap: 24px;
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
@@ -2148,66 +2208,92 @@ main {
   .ds-column-12 .ds-list:not(.ds-list-full-width) {
     grid-template-columns: repeat(3, 1fr); }
   .ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
     display: none; }
   .ds-list:not(.ds-list-images) .ds-list-image {
     display: none; }
   .ds-list a {
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list a {
+      color: #F9F9FA; }
 
 .ds-list-numbers .ds-list-item {
   counter-increment: list; }
 
 .ds-list-numbers .ds-list-item-link {
   padding-inline-start: 41px; }
   .ds-list-numbers .ds-list-item-link::before {
-    background-color: var(--newtab-link-secondary-color);
+    background-color: #50BCB6;
     border-radius: 32px;
     color: #FFF;
     content: counter(list);
     font-size: 17px;
     height: 32px;
     line-height: 32px;
     margin-inline-start: -41px;
     margin-top: 3px;
     position: absolute;
     text-align: center;
     width: 32px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link::before {
+      background-color: #008EA4; }
   .ds-list-numbers .ds-list-item-link:hover::before {
-    background-color: var(--newtab-link-primary-color); }
+    background-color: #45A1FF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:hover::before {
+      background-color: #45A1FF; }
   .ds-list-numbers .ds-list-item-link:active::before {
     background-color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:active::before {
+      background-color: #0060DF; }
 
 .ds-list-borders {
-  border-top: 1px solid var(--newtab-border-secondary-color);
+  border-top: 1px solid #D7D7DB;
   grid-row-gap: 16px;
   padding-top: 16px; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders {
+    border-top: 1px solid #4A4A4F; }
   .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
+      border-bottom: 1px solid #4A4A4F; }
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
@@ -2227,31 +2313,34 @@ main {
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
     line-height: 20px;
     max-height: 2.85714em;
     overflow: hidden;
-    color: var(--newtab-text-secondary-color);
+    color: rgba(249, 249, 250, 0.8);
     margin: 4px 0 8px; }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
     line-height: 20px;
     max-height: 1.42857em;
     overflow: hidden;
     color: #005A71;
     font-size: 13px;
     text-overflow: ellipsis; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-list-item .ds-list-item-context {
+      color: #A7FFFE; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
     flex-direction: column;
     justify-content: space-between; }
   .ds-list-item .ds-list-image {
@@ -2260,17 +2349,17 @@ main {
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 80px;
     margin-inline-start: 14px;
     min-height: 80px; }
   .ds-list-item:hover .ds-list-item-title {
-    color: var(--newtab-link-primary-color); }
+    color: #45A1FF; }
   .ds-list-item:active .ds-list-item-title {
     color: #003EAA; }
 
 .ds-navigation {
   line-height: 32px;
   padding: 4px 0;
   font-size: 14px;
   font-weight: 600; }
@@ -2285,37 +2374,41 @@ main {
     display: inline-block; }
     .ds-navigation ul li::after {
       content: '·';
       padding: 8px;
       color: #737373; }
     .ds-navigation ul li:last-child::after {
       content: none; }
     .ds-navigation ul li a:hover {
-      border-bottom: 1px solid var(--newtab-link-primary-color); }
+      border-bottom: 1px solid #45A1FF; }
       .ds-navigation ul li a:hover:active {
         border-bottom: 1px solid #003EAA; }
     .ds-navigation ul li a:active {
       color: #003EAA; }
   .ds-navigation .ds-header {
     margin-bottom: 8px; }
 
 .ds-section-title {
   text-align: center;
   margin-top: 24px; }
   .ds-section-title .title {
     line-height: 48px;
     font-size: 36px;
     font-weight: 300;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .title {
+      color: #FFF; }
   .ds-section-title .subtitle {
     line-height: 24px;
     font-size: 14px;
     color: #737373;
     margin-top: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .subtitle {
+      color: #D7D7DB; }
 
 .ds-top-sites .ds-header-title {
   vertical-align: middle; }
 
 .ds-top-sites .top-sites {
   padding: 0; }
   .ds-top-sites .top-sites .section-top-bar {
     display: none; }
@@ -2386,35 +2479,108 @@ main {
     width: var(--rightPanelIconWidth);
     height: var(--rightPanelIconWidth); }
   .ds-column-1 .ds-top-sites .top-site-inner .title,
   .ds-column-2 .ds-top-sites .top-site-inner .title,
   .ds-column-3 .ds-top-sites .top-site-inner .title,
   .ds-column-4 .ds-top-sites .top-site-inner .title {
     width: var(--rightPanelIconWidth); }
 
+.ds-hero-item .context-menu-button,
+.ds-list-item .context-menu-button,
+.ds-card .context-menu-button {
+  background-clip: padding-box;
+  background-color: var(--newtab-contextmenu-button-color);
+  background-image: url("chrome://browser/skin/page-action.svg");
+  background-position: 55%;
+  border: 1px solid var(--newtab-border-primary-color);
+  border-radius: 100%;
+  box-shadow: 0 2px rgba(12, 12, 13, 0.1);
+  cursor: pointer;
+  fill: var(--newtab-icon-primary-color);
+  height: 27px;
+  inset-inline-end: -13.5px;
+  opacity: 0;
+  position: absolute;
+  top: -13.5px;
+  transform: scale(0.25);
+  transition-duration: 150ms;
+  transition-property: transform, opacity;
+  width: 27px; }
+  .ds-hero-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-list-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-card .context-menu-button:-moz-any(:active, :focus) {
+    opacity: 1;
+    transform: scale(1); }
+
+.ds-hero-item .context-menu,
+.ds-list-item .context-menu,
+.ds-card .context-menu {
+  opacity: 0; }
+
+.ds-hero-item.active .context-menu,
+.ds-list-item.active .context-menu,
+.ds-card.active .context-menu {
+  opacity: 1; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  margin-inline-end: 5px;
+  margin-inline-start: auto;
+  inset-inline-end: 0;
+  inset-inline-start: auto; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  opacity: 1; }
+
+.ds-hero-item:-moz-any(:hover, :focus, .active),
+.ds-list-item:-moz-any(:hover, :focus, .active),
+.ds-card:-moz-any(:hover, :focus, .active) {
+  outline: none; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-list-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-card:-moz-any(:hover, :focus, .active) .context-menu-button {
+    opacity: 1;
+    transform: scale(1);
+    transition-delay: 333ms; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-list-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-card:-moz-any(:hover, :focus, .active).ds-card-grid-border {
+    box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+    transition: box-shadow 150ms; }
+
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
+      color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 0;
     padding-top: 50%; }
+  .ds-card .ds-card-link {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    height: 100%; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     flex-grow: 1;
     padding: 16px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
@@ -2427,46 +2593,57 @@ main {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #005A71; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card .meta .context, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-card .meta .source {
+        color: #A7FFFE; }
   .ds-card header {
     line-height: 24px;
     font-size: 17px;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card header {
+      color: #F9F9FA; }
   .ds-card p {
     font-size: 14px;
     line-height: 20px;
     color: #737373;
     margin: 8px 0 0; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card p {
+      color: #D7D7DB; }
 
 .ds-message {
   margin: 8px 0 0; }
   .ds-message .title {
     display: flex;
     align-items: center; }
     .ds-message .title .glyph {
       width: 16px;
       height: 16px;
       margin: 0 6px 0 0;
       -moz-context-properties: fill;
-      fill: var(--newtab-icon-secondary-color);
+      fill: #737373;
       background-position: center center;
       background-size: 16px;
       background-repeat: no-repeat; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .glyph {
+        fill: #D7D7DB; }
     .ds-message .title .title-text {
       line-height: 20px;
       font-size: 13px;
       color: #737373;
       font-weight: 600;
       padding-right: 12px; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .title-text {
+        color: #D7D7DB; }
     .ds-message .title .link {
       line-height: 20px;
       font-size: 13px; }
       .ds-message .title .link:hover, .ds-message .title .link:focus {
         text-decoration: underline; }
 
 .impression-observer {
   position: absolute;
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -1840,16 +1840,18 @@ main {
     grid-row-gap: var(--gridRowGap); }
 
 .ds-header {
   color: #737373;
   font-size: 13px;
   font-weight: 600;
   line-height: 20px;
   margin: 8px 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-header {
+    color: #D7D7DB; }
   .ds-header .icon {
     fill: var(--newtab-text-secondary-color); }
 
 .ds-message-container {
   display: none;
   color: #737373;
   font-size: 13px;
   justify-content: center;
@@ -1883,22 +1885,29 @@ main {
 
 .ds-card-grid {
   display: grid;
   grid-gap: 24px;
   margin: 16px 0; }
   .ds-card-grid .ds-card {
     background: #FFF;
     border-radius: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid .ds-card {
+      background: none; }
   .ds-card-grid.ds-card-grid-border .ds-card {
-    box-shadow: var(--newtab-card-shadow); }
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card {
+      box-shadow: 0 1px 4px rgba(12, 12, 13, 0.1);
+      background: #38383D; }
     .ds-card-grid.ds-card-grid-border .ds-card:hover {
-      box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+      box-shadow: 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms;
       outline: none; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card:hover {
+        box-shadow: 0 0 0 5px #4A4A4F; }
     .ds-card-grid.ds-card-grid-border .ds-card .img-wrapper .img {
       border-radius: 4px 4px 0 0; }
   .ds-card-grid.ds-card-grid-no-border .ds-card {
     background: none; }
     .ds-card-grid.ds-card-grid-no-border .ds-card .meta {
       padding: 16px 0; }
   .ds-column-5 .ds-card-grid,
   .ds-column-6 .ds-card-grid,
@@ -1963,55 +1972,74 @@ main {
       box-shadow: none;
       border-radius: 0; }
     .ds-hero .ds-card .meta {
       padding: 0; }
     .ds-hero .ds-card .img-wrapper {
       margin: 0 0 12px; }
   .ds-hero .img-wrapper {
     margin: 0 0 12px; }
+  .ds-hero .ds-hero-item {
+    position: relative; }
   .ds-hero .wrapper {
+    border-top: 1px solid #D7D7DB;
+    border-bottom: 1px solid #D7D7DB;
     color: #737373;
     display: block;
     margin: 12px 0 16px;
     padding: 16px 0;
-    border-top: 1px solid var(--newtab-border-secondary-color);
-    border-bottom: 1px solid var(--newtab-border-secondary-color); }
+    height: 100%; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-top: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-bottom: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      color: #D7D7DB; }
     .ds-hero-no-border .wrapper {
       border-top: 0;
       border-bottom: 0;
       padding: 0 0 8px; }
     .ds-hero .wrapper:hover .meta header {
       color: #0060DF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:hover .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper:active .meta header {
       color: #003EAA; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:active .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper .img-wrapper {
       width: 100%; }
     .ds-hero .wrapper .img {
       height: 0;
       padding-top: 50%; }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 8px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta header {
+          color: #FFF; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .context {
+          color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #005A71;
         margin-bottom: 0;
         overflow-x: hidden;
         text-overflow: ellipsis; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .source {
+          color: #A7FFFE; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-5 .ds-hero .wrapper .img-wrapper,
@@ -2047,24 +2075,34 @@ main {
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
     .ds-column-10 .ds-hero.ds-hero-border,
     .ds-column-11 .ds-hero.ds-hero-border,
     .ds-column-12 .ds-hero.ds-hero-border {
-      border-top: 1px solid var(--newtab-border-secondary-color);
+      border-top: 1px solid #D7D7DB;
       padding: 20px 0; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero.ds-hero-border {
+        border-top: 1px solid #4A4A4F; }
       .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
-        border-bottom: 1px solid var(--newtab-border-secondary-color);
+        border-bottom: 1px solid #D7D7DB;
         margin-bottom: 20px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
+          border-bottom: 1px solid #4A4A4F; }
     .ds-column-9 .ds-hero .wrapper,
     .ds-column-10 .ds-hero .wrapper,
     .ds-column-11 .ds-hero .wrapper,
     .ds-column-12 .ds-hero .wrapper {
       border-top: 0;
       border-bottom: 0;
       margin: 0;
       padding: 0 0 20px;
@@ -2104,29 +2142,51 @@ main {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px; }
-      .ds-column-9 .ds-hero .cards .title,
-      .ds-column-10 .ds-hero .cards .title,
-      .ds-column-11 .ds-hero .cards .title,
-      .ds-column-12 .ds-hero .cards .title {
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:hover {
+        background: none; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card:hover .title {
+          color: #45A1FF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:active .title {
+        color: #0A84FF; }
+      .ds-column-9 .ds-hero .cards .ds-card .title,
+      .ds-column-10 .ds-hero .cards .ds-card .title,
+      .ds-column-11 .ds-hero .cards .ds-card .title,
+      .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
         line-height: 20px;
         max-height: 4.28571em;
         overflow: hidden; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card .title {
+          color: #FFF; }
 
 .ds-hr {
   border: 0;
-  height: 0;
-  border-top: 1px solid var(--newtab-border-secondary-color); }
+  border-top: 1px solid #D7D7DB;
+  height: 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hr {
+    border-top: 1px solid #4A4A4F; }
 
 .ds-list {
   display: grid;
   grid-row-gap: 24px;
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
@@ -2151,66 +2211,92 @@ main {
   .ds-column-12 .ds-list:not(.ds-list-full-width) {
     grid-template-columns: repeat(3, 1fr); }
   .ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
     display: none; }
   .ds-list:not(.ds-list-images) .ds-list-image {
     display: none; }
   .ds-list a {
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list a {
+      color: #F9F9FA; }
 
 .ds-list-numbers .ds-list-item {
   counter-increment: list; }
 
 .ds-list-numbers .ds-list-item-link {
   padding-inline-start: 41px; }
   .ds-list-numbers .ds-list-item-link::before {
-    background-color: var(--newtab-link-secondary-color);
+    background-color: #50BCB6;
     border-radius: 32px;
     color: #FFF;
     content: counter(list);
     font-size: 17px;
     height: 32px;
     line-height: 32px;
     margin-inline-start: -41px;
     margin-top: 3px;
     position: absolute;
     text-align: center;
     width: 32px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link::before {
+      background-color: #008EA4; }
   .ds-list-numbers .ds-list-item-link:hover::before {
-    background-color: var(--newtab-link-primary-color); }
+    background-color: #45A1FF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:hover::before {
+      background-color: #45A1FF; }
   .ds-list-numbers .ds-list-item-link:active::before {
     background-color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:active::before {
+      background-color: #0060DF; }
 
 .ds-list-borders {
-  border-top: 1px solid var(--newtab-border-secondary-color);
+  border-top: 1px solid #D7D7DB;
   grid-row-gap: 16px;
   padding-top: 16px; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders {
+    border-top: 1px solid #4A4A4F; }
   .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
+      border-bottom: 1px solid #4A4A4F; }
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
@@ -2230,31 +2316,34 @@ main {
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
     line-height: 20px;
     max-height: 2.85714em;
     overflow: hidden;
-    color: var(--newtab-text-secondary-color);
+    color: rgba(249, 249, 250, 0.8);
     margin: 4px 0 8px; }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
     line-height: 20px;
     max-height: 1.42857em;
     overflow: hidden;
     color: #005A71;
     font-size: 13px;
     text-overflow: ellipsis; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-list-item .ds-list-item-context {
+      color: #A7FFFE; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
     flex-direction: column;
     justify-content: space-between; }
   .ds-list-item .ds-list-image {
@@ -2263,17 +2352,17 @@ main {
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 80px;
     margin-inline-start: 14px;
     min-height: 80px; }
   .ds-list-item:hover .ds-list-item-title {
-    color: var(--newtab-link-primary-color); }
+    color: #45A1FF; }
   .ds-list-item:active .ds-list-item-title {
     color: #003EAA; }
 
 .ds-navigation {
   line-height: 32px;
   padding: 4px 0;
   font-size: 14px;
   font-weight: 600; }
@@ -2288,37 +2377,41 @@ main {
     display: inline-block; }
     .ds-navigation ul li::after {
       content: '·';
       padding: 8px;
       color: #737373; }
     .ds-navigation ul li:last-child::after {
       content: none; }
     .ds-navigation ul li a:hover {
-      border-bottom: 1px solid var(--newtab-link-primary-color); }
+      border-bottom: 1px solid #45A1FF; }
       .ds-navigation ul li a:hover:active {
         border-bottom: 1px solid #003EAA; }
     .ds-navigation ul li a:active {
       color: #003EAA; }
   .ds-navigation .ds-header {
     margin-bottom: 8px; }
 
 .ds-section-title {
   text-align: center;
   margin-top: 24px; }
   .ds-section-title .title {
     line-height: 48px;
     font-size: 36px;
     font-weight: 300;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .title {
+      color: #FFF; }
   .ds-section-title .subtitle {
     line-height: 24px;
     font-size: 14px;
     color: #737373;
     margin-top: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .subtitle {
+      color: #D7D7DB; }
 
 .ds-top-sites .ds-header-title {
   vertical-align: middle; }
 
 .ds-top-sites .top-sites {
   padding: 0; }
   .ds-top-sites .top-sites .section-top-bar {
     display: none; }
@@ -2389,35 +2482,108 @@ main {
     width: var(--rightPanelIconWidth);
     height: var(--rightPanelIconWidth); }
   .ds-column-1 .ds-top-sites .top-site-inner .title,
   .ds-column-2 .ds-top-sites .top-site-inner .title,
   .ds-column-3 .ds-top-sites .top-site-inner .title,
   .ds-column-4 .ds-top-sites .top-site-inner .title {
     width: var(--rightPanelIconWidth); }
 
+.ds-hero-item .context-menu-button,
+.ds-list-item .context-menu-button,
+.ds-card .context-menu-button {
+  background-clip: padding-box;
+  background-color: var(--newtab-contextmenu-button-color);
+  background-image: url("chrome://browser/skin/page-action.svg");
+  background-position: 55%;
+  border: 1px solid var(--newtab-border-primary-color);
+  border-radius: 100%;
+  box-shadow: 0 2px rgba(12, 12, 13, 0.1);
+  cursor: pointer;
+  fill: var(--newtab-icon-primary-color);
+  height: 27px;
+  inset-inline-end: -13.5px;
+  opacity: 0;
+  position: absolute;
+  top: -13.5px;
+  transform: scale(0.25);
+  transition-duration: 150ms;
+  transition-property: transform, opacity;
+  width: 27px; }
+  .ds-hero-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-list-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-card .context-menu-button:-moz-any(:active, :focus) {
+    opacity: 1;
+    transform: scale(1); }
+
+.ds-hero-item .context-menu,
+.ds-list-item .context-menu,
+.ds-card .context-menu {
+  opacity: 0; }
+
+.ds-hero-item.active .context-menu,
+.ds-list-item.active .context-menu,
+.ds-card.active .context-menu {
+  opacity: 1; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  margin-inline-end: 5px;
+  margin-inline-start: auto;
+  inset-inline-end: 0;
+  inset-inline-start: auto; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  opacity: 1; }
+
+.ds-hero-item:-moz-any(:hover, :focus, .active),
+.ds-list-item:-moz-any(:hover, :focus, .active),
+.ds-card:-moz-any(:hover, :focus, .active) {
+  outline: none; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-list-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-card:-moz-any(:hover, :focus, .active) .context-menu-button {
+    opacity: 1;
+    transform: scale(1);
+    transition-delay: 333ms; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-list-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-card:-moz-any(:hover, :focus, .active).ds-card-grid-border {
+    box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+    transition: box-shadow 150ms; }
+
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
+      color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 0;
     padding-top: 50%; }
+  .ds-card .ds-card-link {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    height: 100%; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     flex-grow: 1;
     padding: 16px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
@@ -2430,46 +2596,57 @@ main {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #005A71; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card .meta .context, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-card .meta .source {
+        color: #A7FFFE; }
   .ds-card header {
     line-height: 24px;
     font-size: 17px;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card header {
+      color: #F9F9FA; }
   .ds-card p {
     font-size: 14px;
     line-height: 20px;
     color: #737373;
     margin: 8px 0 0; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card p {
+      color: #D7D7DB; }
 
 .ds-message {
   margin: 8px 0 0; }
   .ds-message .title {
     display: flex;
     align-items: center; }
     .ds-message .title .glyph {
       width: 16px;
       height: 16px;
       margin: 0 6px 0 0;
       -moz-context-properties: fill;
-      fill: var(--newtab-icon-secondary-color);
+      fill: #737373;
       background-position: center center;
       background-size: 16px;
       background-repeat: no-repeat; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .glyph {
+        fill: #D7D7DB; }
     .ds-message .title .title-text {
       line-height: 20px;
       font-size: 13px;
       color: #737373;
       font-weight: 600;
       padding-right: 12px; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .title-text {
+        color: #D7D7DB; }
     .ds-message .title .link {
       line-height: 20px;
       font-size: 13px; }
       .ds-message .title .link:hover, .ds-message .title .link:focus {
         text-decoration: underline; }
 
 .impression-observer {
   position: absolute;
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -1837,16 +1837,18 @@ main {
     grid-row-gap: var(--gridRowGap); }
 
 .ds-header {
   color: #737373;
   font-size: 13px;
   font-weight: 600;
   line-height: 20px;
   margin: 8px 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-header {
+    color: #D7D7DB; }
   .ds-header .icon {
     fill: var(--newtab-text-secondary-color); }
 
 .ds-message-container {
   display: none;
   color: #737373;
   font-size: 13px;
   justify-content: center;
@@ -1880,22 +1882,29 @@ main {
 
 .ds-card-grid {
   display: grid;
   grid-gap: 24px;
   margin: 16px 0; }
   .ds-card-grid .ds-card {
     background: #FFF;
     border-radius: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid .ds-card {
+      background: none; }
   .ds-card-grid.ds-card-grid-border .ds-card {
-    box-shadow: var(--newtab-card-shadow); }
+    box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1); }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card {
+      box-shadow: 0 1px 4px rgba(12, 12, 13, 0.1);
+      background: #38383D; }
     .ds-card-grid.ds-card-grid-border .ds-card:hover {
-      box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+      box-shadow: 0 0 0 5px #D7D7DB;
       transition: box-shadow 150ms;
       outline: none; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card-grid.ds-card-grid-border .ds-card:hover {
+        box-shadow: 0 0 0 5px #4A4A4F; }
     .ds-card-grid.ds-card-grid-border .ds-card .img-wrapper .img {
       border-radius: 4px 4px 0 0; }
   .ds-card-grid.ds-card-grid-no-border .ds-card {
     background: none; }
     .ds-card-grid.ds-card-grid-no-border .ds-card .meta {
       padding: 16px 0; }
   .ds-column-5 .ds-card-grid,
   .ds-column-6 .ds-card-grid,
@@ -1960,55 +1969,74 @@ main {
       box-shadow: none;
       border-radius: 0; }
     .ds-hero .ds-card .meta {
       padding: 0; }
     .ds-hero .ds-card .img-wrapper {
       margin: 0 0 12px; }
   .ds-hero .img-wrapper {
     margin: 0 0 12px; }
+  .ds-hero .ds-hero-item {
+    position: relative; }
   .ds-hero .wrapper {
+    border-top: 1px solid #D7D7DB;
+    border-bottom: 1px solid #D7D7DB;
     color: #737373;
     display: block;
     margin: 12px 0 16px;
     padding: 16px 0;
-    border-top: 1px solid var(--newtab-border-secondary-color);
-    border-bottom: 1px solid var(--newtab-border-secondary-color); }
+    height: 100%; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-top: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      border-bottom: 1px solid #4A4A4F; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper {
+      color: #D7D7DB; }
     .ds-hero-no-border .wrapper {
       border-top: 0;
       border-bottom: 0;
       padding: 0 0 8px; }
     .ds-hero .wrapper:hover .meta header {
       color: #0060DF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:hover .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper:active .meta header {
       color: #003EAA; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper:active .meta header {
+        color: #45A1FF; }
     .ds-hero .wrapper .img-wrapper {
       width: 100%; }
     .ds-hero .wrapper .img {
       height: 0;
       padding-top: 50%; }
     .ds-hero .wrapper .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between; }
       .ds-hero .wrapper .meta header {
         font-size: 22px;
         line-height: 28px;
         max-height: 5.09091em;
         overflow: hidden;
         color: #0C0C0D;
         margin-bottom: 8px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta header {
+          color: #FFF; }
       .ds-hero .wrapper .meta .context {
         color: #008EA4; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .context {
+          color: #A7FFFE; }
       .ds-hero .wrapper .meta .source {
         font-size: 13px;
         color: #005A71;
         margin-bottom: 0;
         overflow-x: hidden;
         text-overflow: ellipsis; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hero .wrapper .meta .source {
+          color: #A7FFFE; }
   .ds-column-5 .ds-hero .wrapper,
   .ds-column-6 .ds-hero .wrapper,
   .ds-column-7 .ds-hero .wrapper,
   .ds-column-8 .ds-hero .wrapper {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-5 .ds-hero .wrapper .img-wrapper,
@@ -2044,24 +2072,34 @@ main {
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
     .ds-column-10 .ds-hero.ds-hero-border,
     .ds-column-11 .ds-hero.ds-hero-border,
     .ds-column-12 .ds-hero.ds-hero-border {
-      border-top: 1px solid var(--newtab-border-secondary-color);
+      border-top: 1px solid #D7D7DB;
       padding: 20px 0; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero.ds-hero-border, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero.ds-hero-border {
+        border-top: 1px solid #4A4A4F; }
       .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2),
       .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
-        border-bottom: 1px solid var(--newtab-border-secondary-color);
+        border-bottom: 1px solid #D7D7DB;
         margin-bottom: 20px; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2), [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero.ds-hero-border .ds-card:nth-child(-n+2) {
+          border-bottom: 1px solid #4A4A4F; }
     .ds-column-9 .ds-hero .wrapper,
     .ds-column-10 .ds-hero .wrapper,
     .ds-column-11 .ds-hero .wrapper,
     .ds-column-12 .ds-hero .wrapper {
       border-top: 0;
       border-bottom: 0;
       margin: 0;
       padding: 0 0 20px;
@@ -2101,29 +2139,51 @@ main {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px; }
-      .ds-column-9 .ds-hero .cards .title,
-      .ds-column-10 .ds-hero .cards .title,
-      .ds-column-11 .ds-hero .cards .title,
-      .ds-column-12 .ds-hero .cards .title {
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:hover {
+        background: none; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card:hover .title {
+          color: #45A1FF; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-10 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-11 .ds-hero .cards .ds-card:active .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-column-12 .ds-hero .cards .ds-card:active .title {
+        color: #0A84FF; }
+      .ds-column-9 .ds-hero .cards .ds-card .title,
+      .ds-column-10 .ds-hero .cards .ds-card .title,
+      .ds-column-11 .ds-hero .cards .ds-card .title,
+      .ds-column-12 .ds-hero .cards .ds-card .title {
         font-size: 14px;
         line-height: 20px;
         max-height: 4.28571em;
         overflow: hidden; }
+        [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-10 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-11 .ds-hero .cards .ds-card .title, [lwt-newtab-brighttext]:not(.force-light-theme)
+        .ds-column-12 .ds-hero .cards .ds-card .title {
+          color: #FFF; }
 
 .ds-hr {
   border: 0;
-  height: 0;
-  border-top: 1px solid var(--newtab-border-secondary-color); }
+  border-top: 1px solid #D7D7DB;
+  height: 0; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-hr {
+    border-top: 1px solid #4A4A4F; }
 
 .ds-list {
   display: grid;
   grid-row-gap: 24px;
   grid-column-gap: 24px;
   padding-inline-start: 0; }
   .ds-list:not(.ds-list-full-width) .ds-list-item {
     font-size: 14px;
@@ -2148,66 +2208,92 @@ main {
   .ds-column-12 .ds-list:not(.ds-list-full-width) {
     grid-template-columns: repeat(3, 1fr); }
   .ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
     display: none; }
   .ds-list:not(.ds-list-images) .ds-list-image {
     display: none; }
   .ds-list a {
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list a {
+      color: #F9F9FA; }
 
 .ds-list-numbers .ds-list-item {
   counter-increment: list; }
 
 .ds-list-numbers .ds-list-item-link {
   padding-inline-start: 41px; }
   .ds-list-numbers .ds-list-item-link::before {
-    background-color: var(--newtab-link-secondary-color);
+    background-color: #50BCB6;
     border-radius: 32px;
     color: #FFF;
     content: counter(list);
     font-size: 17px;
     height: 32px;
     line-height: 32px;
     margin-inline-start: -41px;
     margin-top: 3px;
     position: absolute;
     text-align: center;
     width: 32px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link::before {
+      background-color: #008EA4; }
   .ds-list-numbers .ds-list-item-link:hover::before {
-    background-color: var(--newtab-link-primary-color); }
+    background-color: #45A1FF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:hover::before {
+      background-color: #45A1FF; }
   .ds-list-numbers .ds-list-item-link:active::before {
     background-color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-numbers .ds-list-item-link:active::before {
+      background-color: #0060DF; }
 
 .ds-list-borders {
-  border-top: 1px solid var(--newtab-border-secondary-color);
+  border-top: 1px solid #D7D7DB;
   grid-row-gap: 16px;
   padding-top: 16px; }
+  [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders {
+    border-top: 1px solid #4A4A4F; }
   .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
   .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
   .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
+      border-bottom: 1px solid #4A4A4F; }
   .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
   .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
-    border-bottom: 1px solid var(--newtab-border-secondary-color);
+    border-bottom: 1px solid #D7D7DB;
     margin-bottom: -1px;
     padding-bottom: 16px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)), [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
+      border-bottom: 1px solid #4A4A4F; }
 
 .ds-list-full-width .ds-list-item {
   font-size: 17px;
   line-height: 24px;
   position: relative; }
 
 .ds-list-full-width .ds-list-item-title {
   font-size: 17px;
@@ -2227,31 +2313,34 @@ main {
     display: flex;
     justify-content: space-between;
     height: 100%; }
   .ds-list-item .ds-list-item-excerpt {
     font-size: 14px;
     line-height: 20px;
     max-height: 2.85714em;
     overflow: hidden;
-    color: var(--newtab-text-secondary-color);
+    color: rgba(249, 249, 250, 0.8);
     margin: 4px 0 8px; }
   .ds-list-item p {
     font-size: 14px;
     line-height: 20px;
     margin: 0; }
   .ds-list-item .ds-list-item-info,
   .ds-list-item .ds-list-item-context {
     font-size: 14px;
     line-height: 20px;
     max-height: 1.42857em;
     overflow: hidden;
     color: #005A71;
     font-size: 13px;
     text-overflow: ellipsis; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-list-item .ds-list-item-info, [lwt-newtab-brighttext]:not(.force-light-theme)
+    .ds-list-item .ds-list-item-context {
+      color: #A7FFFE; }
   .ds-list-item .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px; }
   .ds-list-item .ds-list-item-text {
     display: flex;
     flex-direction: column;
     justify-content: space-between; }
   .ds-list-item .ds-list-image {
@@ -2260,17 +2349,17 @@ main {
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 80px;
     margin-inline-start: 14px;
     min-height: 80px; }
   .ds-list-item:hover .ds-list-item-title {
-    color: var(--newtab-link-primary-color); }
+    color: #45A1FF; }
   .ds-list-item:active .ds-list-item-title {
     color: #003EAA; }
 
 .ds-navigation {
   line-height: 32px;
   padding: 4px 0;
   font-size: 14px;
   font-weight: 600; }
@@ -2285,37 +2374,41 @@ main {
     display: inline-block; }
     .ds-navigation ul li::after {
       content: '·';
       padding: 8px;
       color: #737373; }
     .ds-navigation ul li:last-child::after {
       content: none; }
     .ds-navigation ul li a:hover {
-      border-bottom: 1px solid var(--newtab-link-primary-color); }
+      border-bottom: 1px solid #45A1FF; }
       .ds-navigation ul li a:hover:active {
         border-bottom: 1px solid #003EAA; }
     .ds-navigation ul li a:active {
       color: #003EAA; }
   .ds-navigation .ds-header {
     margin-bottom: 8px; }
 
 .ds-section-title {
   text-align: center;
   margin-top: 24px; }
   .ds-section-title .title {
     line-height: 48px;
     font-size: 36px;
     font-weight: 300;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .title {
+      color: #FFF; }
   .ds-section-title .subtitle {
     line-height: 24px;
     font-size: 14px;
     color: #737373;
     margin-top: 4px; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-section-title .subtitle {
+      color: #D7D7DB; }
 
 .ds-top-sites .ds-header-title {
   vertical-align: middle; }
 
 .ds-top-sites .top-sites {
   padding: 0; }
   .ds-top-sites .top-sites .section-top-bar {
     display: none; }
@@ -2386,35 +2479,108 @@ main {
     width: var(--rightPanelIconWidth);
     height: var(--rightPanelIconWidth); }
   .ds-column-1 .ds-top-sites .top-site-inner .title,
   .ds-column-2 .ds-top-sites .top-site-inner .title,
   .ds-column-3 .ds-top-sites .top-site-inner .title,
   .ds-column-4 .ds-top-sites .top-site-inner .title {
     width: var(--rightPanelIconWidth); }
 
+.ds-hero-item .context-menu-button,
+.ds-list-item .context-menu-button,
+.ds-card .context-menu-button {
+  background-clip: padding-box;
+  background-color: var(--newtab-contextmenu-button-color);
+  background-image: url("chrome://browser/skin/page-action.svg");
+  background-position: 55%;
+  border: 1px solid var(--newtab-border-primary-color);
+  border-radius: 100%;
+  box-shadow: 0 2px rgba(12, 12, 13, 0.1);
+  cursor: pointer;
+  fill: var(--newtab-icon-primary-color);
+  height: 27px;
+  inset-inline-end: -13.5px;
+  opacity: 0;
+  position: absolute;
+  top: -13.5px;
+  transform: scale(0.25);
+  transition-duration: 150ms;
+  transition-property: transform, opacity;
+  width: 27px; }
+  .ds-hero-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-list-item .context-menu-button:-moz-any(:active, :focus),
+  .ds-card .context-menu-button:-moz-any(:active, :focus) {
+    opacity: 1;
+    transform: scale(1); }
+
+.ds-hero-item .context-menu,
+.ds-list-item .context-menu,
+.ds-card .context-menu {
+  opacity: 0; }
+
+.ds-hero-item.active .context-menu,
+.ds-list-item.active .context-menu,
+.ds-card.active .context-menu {
+  opacity: 1; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  margin-inline-end: 5px;
+  margin-inline-start: auto;
+  inset-inline-end: 0;
+  inset-inline-start: auto; }
+
+.ds-hero-item.last-item .context-menu,
+.ds-list-item.last-item .context-menu,
+.ds-card.last-item .context-menu {
+  opacity: 1; }
+
+.ds-hero-item:-moz-any(:hover, :focus, .active),
+.ds-list-item:-moz-any(:hover, :focus, .active),
+.ds-card:-moz-any(:hover, :focus, .active) {
+  outline: none; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-list-item:-moz-any(:hover, :focus, .active) .context-menu-button,
+  .ds-card:-moz-any(:hover, :focus, .active) .context-menu-button {
+    opacity: 1;
+    transform: scale(1);
+    transition-delay: 333ms; }
+  .ds-hero-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-list-item:-moz-any(:hover, :focus, .active).ds-card-grid-border,
+  .ds-card:-moz-any(:hover, :focus, .active).ds-card-grid-border {
+    box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color);
+    transition: box-shadow 150ms; }
+
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
+      color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
     box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
     height: 0;
     padding-top: 50%; }
+  .ds-card .ds-card-link {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    height: 100%; }
   .ds-card .meta {
     display: flex;
     flex-direction: column;
     flex-grow: 1;
     padding: 16px; }
     .ds-card .meta .info-wrap {
       flex-grow: 1; }
     .ds-card .meta .title {
@@ -2427,46 +2593,57 @@ main {
       font-size: 14px;
       line-height: 20px;
       max-height: 4.28571em;
       overflow: hidden; }
     .ds-card .meta .context,
     .ds-card .meta .source {
       font-size: 13px;
       color: #005A71; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card .meta .context, [lwt-newtab-brighttext]:not(.force-light-theme)
+      .ds-card .meta .source {
+        color: #A7FFFE; }
   .ds-card header {
     line-height: 24px;
     font-size: 17px;
     color: #0C0C0D; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card header {
+      color: #F9F9FA; }
   .ds-card p {
     font-size: 14px;
     line-height: 20px;
     color: #737373;
     margin: 8px 0 0; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card p {
+      color: #D7D7DB; }
 
 .ds-message {
   margin: 8px 0 0; }
   .ds-message .title {
     display: flex;
     align-items: center; }
     .ds-message .title .glyph {
       width: 16px;
       height: 16px;
       margin: 0 6px 0 0;
       -moz-context-properties: fill;
-      fill: var(--newtab-icon-secondary-color);
+      fill: #737373;
       background-position: center center;
       background-size: 16px;
       background-repeat: no-repeat; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .glyph {
+        fill: #D7D7DB; }
     .ds-message .title .title-text {
       line-height: 20px;
       font-size: 13px;
       color: #737373;
       font-weight: 600;
       padding-right: 12px; }
+      [lwt-newtab-brighttext]:not(.force-light-theme) .ds-message .title .title-text {
+        color: #D7D7DB; }
     .ds-message .title .link {
       line-height: 20px;
       font-size: 13px; }
       .ds-message .title .link:hover, .ds-message .title .link:focus {
         text-decoration: underline; }
 
 .impression-observer {
   position: absolute;
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -1953,17 +1953,17 @@ function safeURI(url) {
   }
   return isAllowed ? url : "";
 }
 
 /***/ }),
 /* 18 */
 /***/ (function(module) {
 
-module.exports = {"title":"EOYSnippet","description":"Fundraising Snippet","version":"1.0.0","type":"object","definitions":{"plainText":{"description":"Plain text (no HTML allowed)","type":"string"},"richText":{"description":"Text with HTML subset allowed: i, b, u, strong, em, br","type":"string"},"link_url":{"description":"Target for links or buttons","type":"string","format":"uri"}},"properties":{"donation_form_url":{"type":"string","description":"Url to the donation form."},"currency_code":{"type":"string","description":"The code for the currency. Examle gbp, cad, usd.","default":"usd"},"locale":{"type":"string","description":"String for the locale code.","default":"en-US"},"text":{"allOf":[{"$ref":"#/definitions/richText"},{"description":"Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}]},"text_color":{"type":"string","description":"Modify the text message color"},"background_color":{"type":"string","description":"Snippet background color."},"highlight_color":{"type":"string","description":"Paragraph em highlight color."},"donation_amount_first":{"type":"number","description":"First button amount."},"donation_amount_second":{"type":"number","description":"Second button amount."},"donation_amount_third":{"type":"number","description":"Third button amount."},"donation_amount_fourth":{"type":"number","description":"Fourth button amount."},"selected_button":{"type":"string","description":"Default donation_amount_second. Donation amount button that's selected by default.","default":"donation_amount_second"},"icon":{"type":"string","description":"Snippet icon. 64x64px. SVG or PNG preferred."},"title_icon":{"type":"string","description":"Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."},"button_label":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Text for a button next to main snippet text that links to button_url. Requires button_url."}]},"button_color":{"type":"string","description":"The text color of the button. Valid CSS color."},"button_background_color":{"type":"string","description":"The background color of the button. Valid CSS color."},"block_button_text":{"type":"string","description":"Tooltip text used for dismiss button."},"monthly_checkbox_label_text":{"type":"string","description":"Label text for monthly checkbox.","default":"Make my donation monthly"},"test":{"type":"string","description":"Different styles for the snippet. Options are bold and takeover."},"do_not_autoblock":{"type":"boolean","description":"Used to prevent blocking the snippet after the CTA (link or button) has been clicked"},"links":{"additionalProperties":{"url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"The url where the link points to."}]},"metric":{"type":"string","description":"Custom event name sent with telemetry event."},"args":{"type":"string","description":"Additional parameters for link action, example which specific menu the button should open"}}}},"additionalProperties":false,"required":["text","donation_form_url","donation_amount_first","donation_amount_second","donation_amount_third","donation_amount_fourth","button_label","currency_code"],"dependencies":{"button_color":["button_label"],"button_background_color":["button_label"]}};
+module.exports = {"title":"EOYSnippet","description":"Fundraising Snippet","version":"1.0.0","type":"object","definitions":{"plainText":{"description":"Plain text (no HTML allowed)","type":"string"},"richText":{"description":"Text with HTML subset allowed: i, b, u, strong, em, br","type":"string"},"link_url":{"description":"Target for links or buttons","type":"string","format":"uri"}},"properties":{"donation_form_url":{"type":"string","description":"Url to the donation form."},"currency_code":{"type":"string","description":"The code for the currency. Examle gbp, cad, usd.","default":"usd"},"locale":{"type":"string","description":"String for the locale code.","default":"en-US"},"text":{"allOf":[{"$ref":"#/definitions/richText"},{"description":"Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}]},"text_color":{"type":"string","description":"Modify the text message color"},"background_color":{"type":"string","description":"Snippet background color."},"highlight_color":{"type":"string","description":"Paragraph em highlight color."},"donation_amount_first":{"type":"number","description":"First button amount."},"donation_amount_second":{"type":"number","description":"Second button amount."},"donation_amount_third":{"type":"number","description":"Third button amount."},"donation_amount_fourth":{"type":"number","description":"Fourth button amount."},"selected_button":{"type":"string","description":"Default donation_amount_second. Donation amount button that's selected by default.","default":"donation_amount_second"},"icon":{"type":"string","description":"Snippet icon. 64x64px. SVG or PNG preferred."},"title":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Snippet title displayed before snippet text"}]},"title_icon":{"type":"string","description":"Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."},"button_label":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Text for a button next to main snippet text that links to button_url. Requires button_url."}]},"button_color":{"type":"string","description":"The text color of the button. Valid CSS color."},"button_background_color":{"type":"string","description":"The background color of the button. Valid CSS color."},"block_button_text":{"type":"string","description":"Tooltip text used for dismiss button."},"monthly_checkbox_label_text":{"type":"string","description":"Label text for monthly checkbox.","default":"Make my donation monthly"},"test":{"type":"string","description":"Different styles for the snippet. Options are bold and takeover."},"do_not_autoblock":{"type":"boolean","description":"Used to prevent blocking the snippet after the CTA (link or button) has been clicked"},"links":{"additionalProperties":{"url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"The url where the link points to."}]},"metric":{"type":"string","description":"Custom event name sent with telemetry event."},"args":{"type":"string","description":"Additional parameters for link action, example which specific menu the button should open"}}}},"additionalProperties":false,"required":["text","donation_form_url","donation_amount_first","donation_amount_second","donation_amount_third","donation_amount_fourth","button_label","currency_code"],"dependencies":{"button_color":["button_label"],"button_background_color":["button_label"]}};
 
 /***/ }),
 /* 19 */
 /***/ (function(module) {
 
 module.exports = {"title":"SimpleSnippet","description":"A simple template with an icon, text, and optional button.","version":"1.1.1","type":"object","definitions":{"plainText":{"description":"Plain text (no HTML allowed)","type":"string"},"richText":{"description":"Text with HTML subset allowed: i, b, u, strong, em, br","type":"string"},"link_url":{"description":"Target for links or buttons","type":"string","format":"uri"}},"properties":{"title":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Snippet title displayed before snippet text"}]},"text":{"allOf":[{"$ref":"#/definitions/richText"},{"description":"Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}]},"icon":{"type":"string","description":"Snippet icon. 64x64px. SVG or PNG preferred."},"title_icon":{"type":"string","description":"Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."},"button_action":{"type":"string","description":"The type of action the button should trigger."},"button_url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"A url, button_label links to this"}]},"button_action_args":{"type":"string","description":"Additional parameters for button action, example which specific menu the button should open"},"button_label":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Text for a button next to main snippet text that links to button_url. Requires button_url."}]},"button_color":{"type":"string","description":"The text color of the button. Valid CSS color."},"button_background_color":{"type":"string","description":"The background color of the button. Valid CSS color."},"block_button_text":{"type":"string","description":"Tooltip text used for dismiss button.","default":"Remove this"},"tall":{"type":"boolean","description":"To be used by fundraising only, increases height to roughly 120px. Defaults to false."},"do_not_autoblock":{"type":"boolean","description":"Used to prevent blocking the snippet after the CTA (link or button) has been clicked"},"links":{"additionalProperties":{"url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"The url where the link points to."}]},"metric":{"type":"string","description":"Custom event name sent with telemetry event."},"args":{"type":"string","description":"Additional parameters for link action, example which specific menu the button should open"}}},"section_title_icon":{"type":"string","description":"Section title icon. 16x16px. SVG or PNG preferred. section_title_text must also be specified to display."},"section_title_text":{"type":"string","description":"Section title text. section_title_icon must also be specified to display."},"section_title_url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"A url, section_title_text links to this"}]}},"additionalProperties":false,"required":["text"],"dependencies":{"button_action":["button_label"],"button_url":["button_label"],"button_color":["button_label"],"button_background_color":["button_label"],"section_title_url":["section_title_text"]}};
 
 /***/ }),
@@ -2233,17 +2233,17 @@ module.exports = ReactRedux;
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var content_src_components_ASRouterAdmin_ASRouterAdmin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(26);
 /* harmony import */ var content_src_components_ConfirmDialog_ConfirmDialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(28);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var content_src_components_DarkModeMessage_DarkModeMessage__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(29);
 /* harmony import */ var content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(55);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(34);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(37);
 /* harmony import */ var content_src_components_ManualMigration_ManualMigration__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(45);
 /* harmony import */ var common_PrerenderData_jsm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(46);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_10__);
 /* harmony import */ var content_src_components_Search_Search__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(47);
 /* harmony import */ var content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(49);
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
@@ -2385,40 +2385,27 @@ class BaseContent extends react__WEBPACK
     }
   }
 
   openPreferences() {
     this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SETTINGS_OPEN }));
     this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
   }
 
-  disableDarkTheme() {
-    // Dark themes are not supported in discovery stream view
-    // Add force-light-theme class to body tag to disable dark mode. See Bug 1519764
-    const bodyClassNames = global.document.body.classList;
-    if (!bodyClassNames.contains("force-light-theme")) {
-      bodyClassNames.add("force-light-theme");
-    }
-  }
-
   render() {
     const { props } = this;
     const { App } = props;
     const { initialized } = App;
     const prefs = props.Prefs.values;
 
     const shouldBeFixedToTop = common_PrerenderData_jsm__WEBPACK_IMPORTED_MODULE_9__["PrerenderData"].arePrefsValid(name => prefs[name]);
     const noSectionsEnabled = !prefs["feeds.topsites"] && props.Sections.filter(section => section.enabled).length === 0;
     const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
     const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
 
-    if (isDiscoveryStream) {
-      this.disableDarkTheme();
-    }
-
     const outerClassName = ["outer-wrapper", isDiscoveryStream && "ds-outer-wrapper-search-alignment", isDiscoveryStream && "ds-outer-wrapper-breakpoint-override", shouldBeFixedToTop && "fixed-to-top", prefs.showSearch && this.state.fixedSearch && !noSectionsEnabled && "fixed-search", prefs.showSearch && noSectionsEnabled && "only-search"].filter(v => v).join(" ");
 
     return react__WEBPACK_IMPORTED_MODULE_10___default.a.createElement(
       "div",
       null,
       react__WEBPACK_IMPORTED_MODULE_10___default.a.createElement(
         "div",
         { className: outerClassName },
@@ -3871,16 +3858,425 @@ class _DarkModeMessage extends react__WE
 const DarkModeMessage = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])()(_DarkModeMessage);
 
 /***/ }),
 /* 30 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_LinkMenu", function() { return _LinkMenu; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenu", function() { return LinkMenu; });
+/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
+/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
+/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31);
+/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
+/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_3__);
+/* harmony import */ var content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(32);
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_5__);
+
+
+
+
+
+
+
+const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
+
+class _LinkMenu extends react__WEBPACK_IMPORTED_MODULE_5___default.a.PureComponent {
+  getOptions() {
+    const { props } = this;
+    const { site, index, source, isPrivateBrowsingEnabled, siteInfo, platform } = props;
+
+    // Handle special case of default site
+    const propOptions = !site.isDefault || site.searchTopSite ? props.options : DEFAULT_SITE_MENU_OPTIONS;
+
+    const options = propOptions.map(o => content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__["LinkMenuOptions"][o](site, index, source, isPrivateBrowsingEnabled, siteInfo, platform)).map(option => {
+      const { action, impression, id, string_id, type, userEvent } = option;
+      if (!type && id) {
+        option.label = props.intl.formatMessage({ id: string_id || id });
+        option.onClick = () => {
+          props.dispatch(action);
+          if (userEvent) {
+            const userEventData = Object.assign({
+              event: userEvent,
+              source,
+              action_position: index
+            }, siteInfo);
+            props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent(userEventData));
+          }
+          if (impression && props.shouldSendImpressionStats) {
+            props.dispatch(impression);
+          }
+        };
+      }
+      return option;
+    });
+
+    // This is for accessibility to support making each item tabbable.
+    // We want to know which item is the first and which item
+    // is the last, so we can close the context menu accordingly.
+    options[0].first = true;
+    options[options.length - 1].last = true;
+    return options;
+  }
+
+  render() {
+    return react__WEBPACK_IMPORTED_MODULE_5___default.a.createElement(content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__["ContextMenu"], {
+      onUpdate: this.props.onUpdate,
+      onShow: this.props.onShow,
+      options: this.getOptions() });
+  }
+}
+
+const getState = state => ({ isPrivateBrowsingEnabled: state.Prefs.values.isPrivateBrowsingEnabled, platform: state.Prefs.values.platform });
+const LinkMenu = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])(getState)(Object(react_intl__WEBPACK_IMPORTED_MODULE_3__["injectIntl"])(_LinkMenu));
+
+/***/ }),
+/* 31 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return ContextMenu; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenuItem", function() { return ContextMenuItem; });
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
+/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
+
+
+class ContextMenu extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.hideContext = this.hideContext.bind(this);
+    this.onShow = this.onShow.bind(this);
+    this.onClick = this.onClick.bind(this);
+  }
+
+  hideContext() {
+    this.props.onUpdate(false);
+  }
+
+  onShow() {
+    if (this.props.onShow) {
+      this.props.onShow();
+    }
+  }
+
+  componentDidMount() {
+    this.onShow();
+    setTimeout(() => {
+      global.addEventListener("click", this.hideContext);
+    }, 0);
+  }
+
+  componentWillUnmount() {
+    global.removeEventListener("click", this.hideContext);
+  }
+
+  onClick(event) {
+    // Eat all clicks on the context menu so they don't bubble up to window.
+    // This prevents the context menu from closing when clicking disabled items
+    // or the separators.
+    event.stopPropagation();
+  }
+
+  render() {
+    return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
+      "span",
+      { className: "context-menu", onClick: this.onClick },
+      react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
+        "ul",
+        { role: "menu", className: "context-menu-list" },
+        this.props.options.map((option, i) => option.type === "separator" ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("li", { key: i, className: "separator" }) : option.type !== "empty" && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
+      )
+    );
+  }
+}
+
+class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.onClick = this.onClick.bind(this);
+    this.onKeyDown = this.onKeyDown.bind(this);
+  }
+
+  onClick() {
+    this.props.hideContext();
+    this.props.option.onClick();
+  }
+
+  onKeyDown(event) {
+    const { option } = this.props;
+    switch (event.key) {
+      case "Tab":
+        // tab goes down in context menu, shift + tab goes up in context menu
+        // if we're on the last item, one more tab will close the context menu
+        // similarly, if we're on the first item, one more shift + tab will close it
+        if (event.shiftKey && option.first || !event.shiftKey && option.last) {
+          this.props.hideContext();
+        }
+        break;
+      case "Enter":
+        this.props.hideContext();
+        option.onClick();
+        break;
+    }
+  }
+
+  render() {
+    const { option } = this.props;
+    return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
+      "li",
+      { role: "menuitem", className: "context-menu-item" },
+      react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
+        "a",
+        { onClick: this.onClick, onKeyDown: this.onKeyDown, tabIndex: "0", className: option.disabled ? "disabled" : "" },
+        option.icon && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }),
+        option.label
+      )
+    );
+  }
+}
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
+
+/***/ }),
+/* 32 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GetPlatformString", function() { return GetPlatformString; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenuOptions", function() { return LinkMenuOptions; });
+/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
+
+
+const _OpenInPrivateWindow = site => ({
+  id: "menu_action_open_private_window",
+  icon: "new-window-private",
+  action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+    type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_PRIVATE_WINDOW,
+    data: { url: site.url, referrer: site.referrer }
+  }),
+  userEvent: "OPEN_PRIVATE_WINDOW"
+});
+
+const GetPlatformString = platform => {
+  switch (platform) {
+    case "win":
+      return "menu_action_show_file_windows";
+    case "macosx":
+      return "menu_action_show_file_mac_os";
+    case "linux":
+      return "menu_action_show_file_linux";
+    default:
+      return "menu_action_show_file_default";
+  }
+};
+
+/**
+ * List of functions that return items that can be included as menu options in a
+ * LinkMenu. All functions take the site as the first parameter, and optionally
+ * the index of the site.
+ */
+const LinkMenuOptions = {
+  Separator: () => ({ type: "separator" }),
+  EmptyItem: () => ({ type: "empty" }),
+  RemoveBookmark: site => ({
+    id: "menu_action_remove_bookmark",
+    icon: "bookmark-added",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_BOOKMARK_BY_ID,
+      data: site.bookmarkGuid
+    }),
+    userEvent: "BOOKMARK_DELETE"
+  }),
+  AddBookmark: site => ({
+    id: "menu_action_bookmark",
+    icon: "bookmark-hollow",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].BOOKMARK_URL,
+      data: { url: site.url, title: site.title, type: site.type }
+    }),
+    userEvent: "BOOKMARK_ADD"
+  }),
+  OpenInNewWindow: site => ({
+    id: "menu_action_open_new_window",
+    icon: "new-window",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_NEW_WINDOW,
+      data: {
+        referrer: site.referrer,
+        typedBonus: site.typedBonus,
+        url: site.url
+      }
+    }),
+    userEvent: "OPEN_NEW_WINDOW"
+  }),
+  BlockUrl: (site, index, eventSource) => ({
+    id: "menu_action_dismiss",
+    icon: "dismiss",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].BLOCK_URL,
+      data: { url: site.open_url || site.url, pocket_id: site.pocket_id }
+    }),
+    impression: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].ImpressionStats({
+      source: eventSource,
+      block: 0,
+      tiles: [{ id: site.guid, pos: index }]
+    }),
+    userEvent: "BLOCK"
+  }),
+
+  // This is an option for web extentions which will result in remove items from
+  // memory and notify the web extenion, rather than using the built-in block list.
+  WebExtDismiss: (site, index, eventSource) => ({
+    id: "menu_action_webext_dismiss",
+    string_id: "menu_action_dismiss",
+    icon: "dismiss",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].WebExtEvent(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].WEBEXT_DISMISS, {
+      source: eventSource,
+      url: site.url,
+      action_position: index
+    })
+  }),
+  DeleteUrl: (site, index, eventSource, isEnabled, siteInfo) => ({
+    id: "menu_action_delete",
+    icon: "delete",
+    action: {
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DIALOG_OPEN,
+      data: {
+        onConfirm: [common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_HISTORY_URL, data: { url: site.url, pocket_id: site.pocket_id, forceBlock: site.bookmarkGuid } }), common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent(Object.assign({ event: "DELETE", source: eventSource, action_position: index }, siteInfo))],
+        eventSource,
+        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
+        confirm_button_string_id: "menu_action_delete",
+        cancel_button_string_id: "topsites_form_cancel_button",
+        icon: "modal-delete"
+      }
+    },
+    userEvent: "DIALOG_OPEN"
+  }),
+  ShowFile: (site, index, eventSource, isEnabled, siteInfo, platform) => ({
+    id: GetPlatformString(platform),
+    icon: "search",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SHOW_DOWNLOAD_FILE,
+      data: { url: site.url }
+    })
+  }),
+  OpenFile: site => ({
+    id: "menu_action_open_file",
+    icon: "open-file",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_DOWNLOAD_FILE,
+      data: { url: site.url }
+    })
+  }),
+  CopyDownloadLink: site => ({
+    id: "menu_action_copy_download_link",
+    icon: "copy",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].COPY_DOWNLOAD_LINK,
+      data: { url: site.url }
+    })
+  }),
+  GoToDownloadPage: site => ({
+    id: "menu_action_go_to_download_page",
+    icon: "download",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_LINK,
+      data: { url: site.referrer }
+    }),
+    disabled: !site.referrer
+  }),
+  RemoveDownload: site => ({
+    id: "menu_action_remove_download",
+    icon: "delete",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].REMOVE_DOWNLOAD_FILE,
+      data: { url: site.url }
+    })
+  }),
+  PinTopSite: ({ url, searchTopSite, label }, index) => ({
+    id: "menu_action_pin",
+    icon: "pin",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_PIN,
+      data: {
+        site: Object.assign({
+          url
+        }, searchTopSite && { searchTopSite, label }),
+        index
+      }
+    }),
+    userEvent: "PIN"
+  }),
+  UnpinTopSite: site => ({
+    id: "menu_action_unpin",
+    icon: "unpin",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_UNPIN,
+      data: { site: { url: site.url } }
+    }),
+    userEvent: "UNPIN"
+  }),
+  SaveToPocket: (site, index, eventSource) => ({
+    id: "menu_action_save_to_pocket",
+    icon: "pocket-save",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SAVE_TO_POCKET,
+      data: { site: { url: site.url, title: site.title } }
+    }),
+    impression: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].ImpressionStats({
+      source: eventSource,
+      pocket: 0,
+      tiles: [{ id: site.guid, pos: index }]
+    }),
+    userEvent: "SAVE_TO_POCKET"
+  }),
+  DeleteFromPocket: site => ({
+    id: "menu_action_delete_pocket",
+    icon: "delete",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_FROM_POCKET,
+      data: { pocket_id: site.pocket_id }
+    }),
+    userEvent: "DELETE_FROM_POCKET"
+  }),
+  ArchiveFromPocket: site => ({
+    id: "menu_action_archive_pocket",
+    icon: "check",
+    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].ARCHIVE_FROM_POCKET,
+      data: { pocket_id: site.pocket_id }
+    }),
+    userEvent: "ARCHIVE_FROM_POCKET"
+  }),
+  EditTopSite: (site, index) => ({
+    id: "edit_topsites_button_text",
+    icon: "edit",
+    action: {
+      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_EDIT,
+      data: { index }
+    }
+  }),
+  CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
+  CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index),
+  CheckSavedToPocket: (site, index) => site.pocket_id ? LinkMenuOptions.DeleteFromPocket(site) : LinkMenuOptions.SaveToPocket(site, index),
+  CheckBookmarkOrArchive: site => site.pocket_id ? LinkMenuOptions.ArchiveFromPocket(site) : LinkMenuOptions.CheckBookmark(site),
+  OpenInPrivateWindow: (site, index, eventSource, isEnabled) => isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem()
+};
+
+/***/ }),
+/* 33 */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "INTERSECTION_RATIO", function() { return INTERSECTION_RATIO; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImpressionStats", function() { return ImpressionStats; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 
 
 
@@ -4030,37 +4426,37 @@ ImpressionStats.defaultProps = {
   IntersectionObserver: global.IntersectionObserver,
   document: global.document,
   rows: [],
   source: ""
 };
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 31 */
+/* 34 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_TopSites", function() { return _TopSites; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSites", function() { return TopSites; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(32);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(33);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(38);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(35);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(40);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_6__);
-/* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(40);
+/* harmony import */ var _SearchShortcutsForm__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(42);
 /* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(59);
 /* harmony import */ var _TopSiteForm__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(61);
-/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(41);
+/* harmony import */ var _TopSite__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(43);
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 
 
 
 
 
 
@@ -4239,17 +4635,17 @@ class _TopSites extends react__WEBPACK_I
 const TopSites = Object(react_redux__WEBPACK_IMPORTED_MODULE_4__["connect"])(state => ({
   TopSites: state.TopSites,
   Prefs: state.Prefs,
   TopSitesRows: state.Prefs.values.topSitesRows
 }))(Object(react_intl__WEBPACK_IMPORTED_MODULE_5__["injectIntl"])(_TopSites));
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 32 */
+/* 35 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SOURCE", function() { return TOP_SITES_SOURCE; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS", function() { return TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MIN_RICH_FAVICON_SIZE", function() { return MIN_RICH_FAVICON_SIZE; });
@@ -4259,31 +4655,31 @@ const TOP_SITES_CONTEXT_MENU_OPTIONS = [
 // the special top site for search shortcut experiment can only have the option to unpin (which removes) the topsite
 const TOP_SITES_SEARCH_SHORTCUTS_CONTEXT_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "BlockUrl"];
 // minimum size necessary to show a rich icon instead of a screenshot
 const MIN_RICH_FAVICON_SIZE = 96;
 // minimum size necessary to show any icon in the top left corner with a screenshot
 const MIN_CORNER_FAVICON_SIZE = 16;
 
 /***/ }),
-/* 33 */
+/* 36 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_CollapsibleSection", function() { return _CollapsibleSection; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CollapsibleSection", function() { return CollapsibleSection; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(34);
+/* harmony import */ var content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(37);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(35);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(37);
+/* harmony import */ var content_src_components_SectionMenu_SectionMenu__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(38);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(39);
 
 
 
 
 
 
 
 const VISIBLE = "visible";
@@ -4526,17 +4922,17 @@ class _CollapsibleSection extends react_
   },
   Prefs: { values: {} }
 };
 
 const CollapsibleSection = Object(react_intl__WEBPACK_IMPORTED_MODULE_0__["injectIntl"])(_CollapsibleSection);
 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
 
 /***/ }),
-/* 34 */
+/* 37 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundaryFallback", function() { return ErrorBoundaryFallback; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorBoundary", function() { return ErrorBoundary; });
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_0__);
@@ -4613,30 +5009,30 @@ class ErrorBoundary extends react__WEBPA
 
     return react__WEBPACK_IMPORTED_MODULE_1___default.a.createElement(this.props.FallbackComponent, { className: this.props.className });
   }
 }
 
 ErrorBoundary.defaultProps = { FallbackComponent: ErrorBoundaryFallback };
 
 /***/ }),
-/* 35 */
+/* 38 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_SectionMenu", function() { return _SectionMenu; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenu", function() { return SectionMenu; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(37);
+/* harmony import */ var content_src_lib_section_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(39);
 
 
 
 
 
 
 const DEFAULT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "RemoveSection", "CheckCollapsed", "Separator", "ManageSection"];
 const WEBEXT_SECTION_MENU_OPTIONS = ["MoveUp", "MoveDown", "Separator", "CheckCollapsed", "Separator", "ManageWebExtension"];
@@ -4685,116 +5081,17 @@ class _SectionMenu extends react__WEBPAC
       onUpdate: this.props.onUpdate,
       options: this.getOptions() });
   }
 }
 
 const SectionMenu = Object(react_intl__WEBPACK_IMPORTED_MODULE_2__["injectIntl"])(_SectionMenu);
 
 /***/ }),
-/* 36 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenu", function() { return ContextMenu; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ContextMenuItem", function() { return ContextMenuItem; });
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(10);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
-
-
-class ContextMenu extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
-  constructor(props) {
-    super(props);
-    this.hideContext = this.hideContext.bind(this);
-    this.onClick = this.onClick.bind(this);
-  }
-
-  hideContext() {
-    this.props.onUpdate(false);
-  }
-
-  componentDidMount() {
-    setTimeout(() => {
-      global.addEventListener("click", this.hideContext);
-    }, 0);
-  }
-
-  componentWillUnmount() {
-    global.removeEventListener("click", this.hideContext);
-  }
-
-  onClick(event) {
-    // Eat all clicks on the context menu so they don't bubble up to window.
-    // This prevents the context menu from closing when clicking disabled items
-    // or the separators.
-    event.stopPropagation();
-  }
-
-  render() {
-    return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
-      "span",
-      { className: "context-menu", onClick: this.onClick },
-      react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
-        "ul",
-        { role: "menu", className: "context-menu-list" },
-        this.props.options.map((option, i) => option.type === "separator" ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("li", { key: i, className: "separator" }) : option.type !== "empty" && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(ContextMenuItem, { key: i, option: option, hideContext: this.hideContext }))
-      )
-    );
-  }
-}
-
-class ContextMenuItem extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
-  constructor(props) {
-    super(props);
-    this.onClick = this.onClick.bind(this);
-    this.onKeyDown = this.onKeyDown.bind(this);
-  }
-
-  onClick() {
-    this.props.hideContext();
-    this.props.option.onClick();
-  }
-
-  onKeyDown(event) {
-    const { option } = this.props;
-    switch (event.key) {
-      case "Tab":
-        // tab goes down in context menu, shift + tab goes up in context menu
-        // if we're on the last item, one more tab will close the context menu
-        // similarly, if we're on the first item, one more shift + tab will close it
-        if (event.shiftKey && option.first || !event.shiftKey && option.last) {
-          this.props.hideContext();
-        }
-        break;
-      case "Enter":
-        this.props.hideContext();
-        option.onClick();
-        break;
-    }
-  }
-
-  render() {
-    const { option } = this.props;
-    return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
-      "li",
-      { role: "menuitem", className: "context-menu-item" },
-      react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(
-        "a",
-        { onClick: this.onClick, onKeyDown: this.onKeyDown, tabIndex: "0", className: option.disabled ? "disabled" : "" },
-        option.icon && react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("span", { className: `icon icon-spacer icon-${option.icon}` }),
-        option.label
-      )
-    );
-  }
-}
-/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
-
-/***/ }),
-/* 37 */
+/* 39 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionMenuOptions", function() { return SectionMenuOptions; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 
 
@@ -4873,24 +5170,24 @@ const SectionMenuOptions = {
       data: { url: section.privacyNoticeURL }
     }),
     userEvent: "MENU_PRIVACY_NOTICE"
   }),
   CheckCollapsed: section => section.collapsed ? SectionMenuOptions.ExpandSection(section) : SectionMenuOptions.CollapseSection(section)
 };
 
 /***/ }),
-/* 38 */
+/* 40 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComponentPerfTimer", function() { return ComponentPerfTimer; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(41);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
 
 
 
 
 // Currently record only a fixed set of sections. This will prevent data
 // from custom sections from showing up or from topstories.
@@ -5047,17 +5344,17 @@ class ComponentPerfTimer extends react__
       this._ensureFirstRenderTsRecorded();
       this._maybeSendBadStateEvent();
     }
     return this.props.children;
   }
 }
 
 /***/ }),
-/* 39 */
+/* 41 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_PerfService", function() { return _PerfService; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "perfService", function() { return perfService; });
 
 
@@ -5183,28 +5480,28 @@ function _PerfService(options) {
     let mostRecentEntry = entries[entries.length - 1];
     return this._perf.timeOrigin + mostRecentEntry.startTime;
   }
 };
 
 var perfService = new _PerfService();
 
 /***/ }),
-/* 40 */
+/* 42 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SearchShortcutsForm", function() { return SearchShortcutsForm; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(32);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(35);
 
 
 
 
 
 class SelectableSearchShortcut extends react__WEBPACK_IMPORTED_MODULE_2___default.a.PureComponent {
   render() {
     const { shortcut, selected } = this.props;
@@ -5363,31 +5660,31 @@ class SearchShortcutsForm extends react_
           react__WEBPACK_IMPORTED_MODULE_2___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__["FormattedMessage"], { id: "topsites_form_save_button" })
         )
       )
     );
   }
 }
 
 /***/ }),
-/* 41 */
+/* 43 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteLink", function() { return TopSiteLink; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSite", function() { return TopSite; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSitePlaceholder", function() { return TopSitePlaceholder; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_TopSiteList", function() { return _TopSiteList; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteList", function() { return TopSiteList; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_1__);
-/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(32);
-/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(42);
+/* harmony import */ var _TopSitesConstants__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(35);
+/* harmony import */ var content_src_components_LinkMenu_LinkMenu__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(30);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_4__);
 /* harmony import */ var content_src_lib_screenshot_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(44);
 /* harmony import */ var common_Reducers_jsm__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(59);
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 
 
@@ -5906,317 +6203,16 @@ class _TopSiteList extends react__WEBPAC
       topSitesUI
     );
   }
 }
 
 const TopSiteList = Object(react_intl__WEBPACK_IMPORTED_MODULE_1__["injectIntl"])(_TopSiteList);
 
 /***/ }),
-/* 42 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_LinkMenu", function() { return _LinkMenu; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenu", function() { return LinkMenu; });
-/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
-/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_1__);
-/* harmony import */ var content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(36);
-/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
-/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_3__);
-/* harmony import */ var content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(43);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10);
-/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_5__);
-
-
-
-
-
-
-
-const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
-
-class _LinkMenu extends react__WEBPACK_IMPORTED_MODULE_5___default.a.PureComponent {
-  getOptions() {
-    const { props } = this;
-    const { site, index, source, isPrivateBrowsingEnabled, siteInfo, platform } = props;
-
-    // Handle special case of default site
-    const propOptions = !site.isDefault || site.searchTopSite ? props.options : DEFAULT_SITE_MENU_OPTIONS;
-
-    const options = propOptions.map(o => content_src_lib_link_menu_options__WEBPACK_IMPORTED_MODULE_4__["LinkMenuOptions"][o](site, index, source, isPrivateBrowsingEnabled, siteInfo, platform)).map(option => {
-      const { action, impression, id, string_id, type, userEvent } = option;
-      if (!type && id) {
-        option.label = props.intl.formatMessage({ id: string_id || id });
-        option.onClick = () => {
-          props.dispatch(action);
-          if (userEvent) {
-            const userEventData = Object.assign({
-              event: userEvent,
-              source,
-              action_position: index
-            }, siteInfo);
-            props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent(userEventData));
-          }
-          if (impression && props.shouldSendImpressionStats) {
-            props.dispatch(impression);
-          }
-        };
-      }
-      return option;
-    });
-
-    // This is for accessibility to support making each item tabbable.
-    // We want to know which item is the first and which item
-    // is the last, so we can close the context menu accordingly.
-    options[0].first = true;
-    options[options.length - 1].last = true;
-    return options;
-  }
-
-  render() {
-    return react__WEBPACK_IMPORTED_MODULE_5___default.a.createElement(content_src_components_ContextMenu_ContextMenu__WEBPACK_IMPORTED_MODULE_2__["ContextMenu"], {
-      onUpdate: this.props.onUpdate,
-      options: this.getOptions() });
-  }
-}
-
-const getState = state => ({ isPrivateBrowsingEnabled: state.Prefs.values.isPrivateBrowsingEnabled, platform: state.Prefs.values.platform });
-const LinkMenu = Object(react_redux__WEBPACK_IMPORTED_MODULE_1__["connect"])(getState)(Object(react_intl__WEBPACK_IMPORTED_MODULE_3__["injectIntl"])(_LinkMenu));
-
-/***/ }),
-/* 43 */
-/***/ (function(module, __webpack_exports__, __webpack_require__) {
-
-"use strict";
-__webpack_require__.r(__webpack_exports__);
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GetPlatformString", function() { return GetPlatformString; });
-/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LinkMenuOptions", function() { return LinkMenuOptions; });
-/* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-
-
-const _OpenInPrivateWindow = site => ({
-  id: "menu_action_open_private_window",
-  icon: "new-window-private",
-  action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-    type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_PRIVATE_WINDOW,
-    data: { url: site.url, referrer: site.referrer }
-  }),
-  userEvent: "OPEN_PRIVATE_WINDOW"
-});
-
-const GetPlatformString = platform => {
-  switch (platform) {
-    case "win":
-      return "menu_action_show_file_windows";
-    case "macosx":
-      return "menu_action_show_file_mac_os";
-    case "linux":
-      return "menu_action_show_file_linux";
-    default:
-      return "menu_action_show_file_default";
-  }
-};
-
-/**
- * List of functions that return items that can be included as menu options in a
- * LinkMenu. All functions take the site as the first parameter, and optionally
- * the index of the site.
- */
-const LinkMenuOptions = {
-  Separator: () => ({ type: "separator" }),
-  EmptyItem: () => ({ type: "empty" }),
-  RemoveBookmark: site => ({
-    id: "menu_action_remove_bookmark",
-    icon: "bookmark-added",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_BOOKMARK_BY_ID,
-      data: site.bookmarkGuid
-    }),
-    userEvent: "BOOKMARK_DELETE"
-  }),
-  AddBookmark: site => ({
-    id: "menu_action_bookmark",
-    icon: "bookmark-hollow",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].BOOKMARK_URL,
-      data: { url: site.url, title: site.title, type: site.type }
-    }),
-    userEvent: "BOOKMARK_ADD"
-  }),
-  OpenInNewWindow: site => ({
-    id: "menu_action_open_new_window",
-    icon: "new-window",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_NEW_WINDOW,
-      data: {
-        referrer: site.referrer,
-        typedBonus: site.typedBonus,
-        url: site.url
-      }
-    }),
-    userEvent: "OPEN_NEW_WINDOW"
-  }),
-  BlockUrl: (site, index, eventSource) => ({
-    id: "menu_action_dismiss",
-    icon: "dismiss",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].BLOCK_URL,
-      data: { url: site.open_url || site.url, pocket_id: site.pocket_id }
-    }),
-    impression: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].ImpressionStats({
-      source: eventSource,
-      block: 0,
-      tiles: [{ id: site.guid, pos: index }]
-    }),
-    userEvent: "BLOCK"
-  }),
-
-  // This is an option for web extentions which will result in remove items from
-  // memory and notify the web extenion, rather than using the built-in block list.
-  WebExtDismiss: (site, index, eventSource) => ({
-    id: "menu_action_webext_dismiss",
-    string_id: "menu_action_dismiss",
-    icon: "dismiss",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].WebExtEvent(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].WEBEXT_DISMISS, {
-      source: eventSource,
-      url: site.url,
-      action_position: index
-    })
-  }),
-  DeleteUrl: (site, index, eventSource, isEnabled, siteInfo) => ({
-    id: "menu_action_delete",
-    icon: "delete",
-    action: {
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DIALOG_OPEN,
-      data: {
-        onConfirm: [common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({ type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_HISTORY_URL, data: { url: site.url, pocket_id: site.pocket_id, forceBlock: site.bookmarkGuid } }), common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent(Object.assign({ event: "DELETE", source: eventSource, action_position: index }, siteInfo))],
-        eventSource,
-        body_string_id: ["confirm_history_delete_p1", "confirm_history_delete_notice_p2"],
-        confirm_button_string_id: "menu_action_delete",
-        cancel_button_string_id: "topsites_form_cancel_button",
-        icon: "modal-delete"
-      }
-    },
-    userEvent: "DIALOG_OPEN"
-  }),
-  ShowFile: (site, index, eventSource, isEnabled, siteInfo, platform) => ({
-    id: GetPlatformString(platform),
-    icon: "search",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SHOW_DOWNLOAD_FILE,
-      data: { url: site.url }
-    })
-  }),
-  OpenFile: site => ({
-    id: "menu_action_open_file",
-    icon: "open-file",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_DOWNLOAD_FILE,
-      data: { url: site.url }
-    })
-  }),
-  CopyDownloadLink: site => ({
-    id: "menu_action_copy_download_link",
-    icon: "copy",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].COPY_DOWNLOAD_LINK,
-      data: { url: site.url }
-    })
-  }),
-  GoToDownloadPage: site => ({
-    id: "menu_action_go_to_download_page",
-    icon: "download",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].OPEN_LINK,
-      data: { url: site.referrer }
-    }),
-    disabled: !site.referrer
-  }),
-  RemoveDownload: site => ({
-    id: "menu_action_remove_download",
-    icon: "delete",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].OnlyToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].REMOVE_DOWNLOAD_FILE,
-      data: { url: site.url }
-    })
-  }),
-  PinTopSite: ({ url, searchTopSite, label }, index) => ({
-    id: "menu_action_pin",
-    icon: "pin",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_PIN,
-      data: {
-        site: Object.assign({
-          url
-        }, searchTopSite && { searchTopSite, label }),
-        index
-      }
-    }),
-    userEvent: "PIN"
-  }),
-  UnpinTopSite: site => ({
-    id: "menu_action_unpin",
-    icon: "unpin",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_UNPIN,
-      data: { site: { url: site.url } }
-    }),
-    userEvent: "UNPIN"
-  }),
-  SaveToPocket: (site, index, eventSource) => ({
-    id: "menu_action_save_to_pocket",
-    icon: "pocket-save",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].SAVE_TO_POCKET,
-      data: { site: { url: site.url, title: site.title } }
-    }),
-    impression: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].ImpressionStats({
-      source: eventSource,
-      pocket: 0,
-      tiles: [{ id: site.guid, pos: index }]
-    }),
-    userEvent: "SAVE_TO_POCKET"
-  }),
-  DeleteFromPocket: site => ({
-    id: "menu_action_delete_pocket",
-    icon: "delete",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].DELETE_FROM_POCKET,
-      data: { pocket_id: site.pocket_id }
-    }),
-    userEvent: "DELETE_FROM_POCKET"
-  }),
-  ArchiveFromPocket: site => ({
-    id: "menu_action_archive_pocket",
-    icon: "check",
-    action: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].AlsoToMain({
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].ARCHIVE_FROM_POCKET,
-      data: { pocket_id: site.pocket_id }
-    }),
-    userEvent: "ARCHIVE_FROM_POCKET"
-  }),
-  EditTopSite: (site, index) => ({
-    id: "edit_topsites_button_text",
-    icon: "edit",
-    action: {
-      type: common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionTypes"].TOP_SITES_EDIT,
-      data: { index }
-    }
-  }),
-  CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
-  CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index),
-  CheckSavedToPocket: (site, index) => site.pocket_id ? LinkMenuOptions.DeleteFromPocket(site) : LinkMenuOptions.SaveToPocket(site, index),
-  CheckBookmarkOrArchive: site => site.pocket_id ? LinkMenuOptions.ArchiveFromPocket(site) : LinkMenuOptions.CheckBookmark(site),
-  OpenInPrivateWindow: (site, index, eventSource, isEnabled) => isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem()
-};
-
-/***/ }),
 /* 44 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScreenshotUtils", function() { return ScreenshotUtils; });
 /**
  * List of helper functions for screenshot-based images.
@@ -6679,26 +6675,26 @@ const IS_NEWTAB = global.document && glo
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Section", function() { return Section; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionIntl", function() { return SectionIntl; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Sections", function() { return _Sections; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Sections", function() { return Sections; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
 /* harmony import */ var content_src_components_Card_Card__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(60);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
 /* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_intl__WEBPACK_IMPORTED_MODULE_2__);
-/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(33);
-/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(38);
+/* harmony import */ var content_src_components_CollapsibleSection_CollapsibleSection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(36);
+/* harmony import */ var content_src_components_ComponentPerfTimer_ComponentPerfTimer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(40);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(24);
 /* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(react_redux__WEBPACK_IMPORTED_MODULE_5__);
 /* harmony import */ var content_src_components_MoreRecommendations_MoreRecommendations__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(50);
 /* harmony import */ var content_src_components_PocketLoggedInCta_PocketLoggedInCta__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(51);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(10);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_8__);
 /* harmony import */ var content_src_components_Topics_Topics__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(52);
-/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(31);
+/* harmony import */ var content_src_components_TopSites_TopSites__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(34);
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 
 
 
 
 
 
@@ -7141,17 +7137,17 @@ class Topics extends react__WEBPACK_IMPO
 /***/ }),
 /* 53 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* WEBPACK VAR INJECTION */(function(global) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DetectUserSessionStart", function() { return DetectUserSessionStart; });
 /* harmony import */ var common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
-/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39);
+/* harmony import */ var common_PerfService_jsm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(41);
 
 
 
 const VISIBLE = "visible";
 const VISIBILITY_CHANGE_EVENT = "visibilitychange";
 
 class DetectUserSessionStart {
   constructor(store, options = {}) {
@@ -7240,23 +7236,111 @@ function enableASRouterContent(store, as
 /* 55 */
 /***/ (function(module, __webpack_exports__, __webpack_require__) {
 
 "use strict";
 
 // EXTERNAL MODULE: ./common/Actions.jsm
 var Actions = __webpack_require__(2);
 
-// EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
-var ImpressionStats = __webpack_require__(30);
+// EXTERNAL MODULE: external "ReactIntl"
+var external_ReactIntl_ = __webpack_require__(5);
+
+// EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
+var LinkMenu = __webpack_require__(30);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(10);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
+// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
+
+
+
+
+class DSLinkMenu_DSLinkMenu extends external_React_default.a.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = {
+      activeCard: null,
+      showContextMenu: false
+    };
+    this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
+    this.onMenuUpdate = this.onMenuUpdate.bind(this);
+    this.onMenuShow = this.onMenuShow.bind(this);
+    this.contextMenuButtonRef = external_React_default.a.createRef();
+  }
+
+  onMenuButtonClick(event) {
+    event.preventDefault();
+    this.setState({
+      activeCard: this.props.index,
+      showContextMenu: true
+    });
+  }
+
+  onMenuUpdate(showContextMenu) {
+    if (!showContextMenu) {
+      const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
+      dsLinkMenuHostDiv.parentElement.classList.remove("active", "last-item");
+    }
+    this.setState({ showContextMenu });
+  }
+
+  onMenuShow() {
+    const dsLinkMenuHostDiv = this.contextMenuButtonRef.current.parentElement;
+    if (window.scrollMaxX > 0) {
+      dsLinkMenuHostDiv.parentElement.classList.add("last-item");
+    }
+    dsLinkMenuHostDiv.parentElement.classList.add("active");
+  }
+
+  render() {
+    const { index, dispatch } = this.props;
+    const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
+    const TOP_STORIES_SOURCE = "TOP_STORIES";
+    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow"];
+    const title = this.props.title || this.props.source;
+
+    return external_React_default.a.createElement(
+      "div",
+      null,
+      external_React_default.a.createElement(
+        "button",
+        { ref: this.contextMenuButtonRef,
+          className: "context-menu-button icon",
+          title: this.props.intl.formatMessage({ id: "context_menu_title" }),
+          onClick: this.onMenuButtonClick },
+        external_React_default.a.createElement(
+          "span",
+          { className: "sr-only" },
+          external_React_default.a.createElement(external_ReactIntl_["FormattedMessage"], { id: "context_menu_button_sr", values: { title } })
+        )
+      ),
+      isContextMenuOpen && external_React_default.a.createElement(LinkMenu["LinkMenu"], {
+        dispatch: dispatch,
+        index: index,
+        source: TOP_STORIES_SOURCE,
+        onUpdate: this.onMenuUpdate,
+        onShow: this.onMenuShow,
+        options: TOP_STORIES_CONTEXT_MENU_OPTIONS,
+        site: {
+          referrer: "https://getpocket.com/recommendations",
+          title: this.props.title,
+          type: this.props.type,
+          url: this.props.url
+        } })
+    );
+  }
+}
+
+const DSLinkMenu = Object(external_ReactIntl_["injectIntl"])(DSLinkMenu_DSLinkMenu);
+// EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
+var ImpressionStats = __webpack_require__(33);
+
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
 
 
 
 class SafeAnchor_SafeAnchor extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onClick = this.onClick.bind(this);
@@ -7310,16 +7394,17 @@ class SafeAnchor_SafeAnchor extends exte
   }
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
 
 
 
 
 
+
 class DSCard_DSCard extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
 
     this.onLinkClick = this.onLinkClick.bind(this);
   }
 
   onLinkClick(event) {
@@ -7335,69 +7420,81 @@ class DSCard_DSCard extends external_Rea
         click: 0,
         tiles: [{ id: this.props.id, pos: this.props.pos }]
       }));
     }
   }
 
   render() {
     return external_React_default.a.createElement(
-      SafeAnchor_SafeAnchor,
-      {
-        className: "ds-card",
-        dispatch: this.props.dispatch,
-        onLinkClick: this.onLinkClick,
-        url: this.props.url },
+      "div",
+      { className: "ds-card" },
       external_React_default.a.createElement(
-        "div",
-        { className: "img-wrapper" },
-        external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${this.props.image_src}` } })
-      ),
-      external_React_default.a.createElement(
-        "div",
-        { className: "meta" },
+        SafeAnchor_SafeAnchor,
+        {
+          className: "ds-card-link",
+          dispatch: this.props.dispatch,
+          onLinkClick: this.onLinkClick,
+          url: this.props.url },
+        external_React_default.a.createElement(
+          "div",
+          { className: "img-wrapper" },
+          external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${this.props.image_src}` } })
+        ),
         external_React_default.a.createElement(
           "div",
-          { className: "info-wrap" },
+          { className: "meta" },
+          external_React_default.a.createElement(
+            "div",
+            { className: "info-wrap" },
+            external_React_default.a.createElement(
+              "header",
+              { className: "title" },
+              this.props.title
+            ),
+            this.props.excerpt && external_React_default.a.createElement(
+              "p",
+              { className: "excerpt" },
+              this.props.excerpt
+            )
+          ),
           external_React_default.a.createElement(
-            "header",
-            { className: "title" },
-            this.props.title
-          ),
-          this.props.excerpt && external_React_default.a.createElement(
             "p",
-            { className: "excerpt" },
-            this.props.excerpt
+            null,
+            this.props.context && external_React_default.a.createElement(
+              "span",
+              null,
+              external_React_default.a.createElement(
+                "span",
+                { className: "context" },
+                this.props.context
+              ),
+              external_React_default.a.createElement("br", null)
+            ),
+            external_React_default.a.createElement(
+              "span",
+              { className: "source" },
+              this.props.source
+            )
           )
         ),
-        external_React_default.a.createElement(
-          "p",
-          null,
-          this.props.context && external_React_default.a.createElement(
-            "span",
-            null,
-            external_React_default.a.createElement(
-              "span",
-              { className: "context" },
-              this.props.context
-            ),
-            external_React_default.a.createElement("br", null)
-          ),
-          external_React_default.a.createElement(
-            "span",
-            { className: "source" },
-            this.props.source
-          )
-        )
+        external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
+          campaignId: this.props.campaignId,
+          rows: [{ id: this.props.id, pos: this.props.pos }],
+          dispatch: this.props.dispatch,
+          source: this.props.type })
       ),
-      external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
-        campaignId: this.props.campaignId,
-        rows: [{ id: this.props.id, pos: this.props.pos }],
+      external_React_default.a.createElement(DSLinkMenu, {
+        index: this.props.index,
         dispatch: this.props.dispatch,
-        source: this.props.type })
+        intl: this.props.intl,
+        url: this.props.url,
+        title: this.props.title,
+        source: this.props.source,
+        type: this.props.type })
     );
   }
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
 
 
 
 class CardGrid_CardGrid extends external_React_default.a.PureComponent {
@@ -7484,16 +7581,17 @@ class DSMessage_DSMessage extends extern
 }
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/List/List.jsx
 
 
 
 
 
 
+
 /**
  * @note exported for testing only
  */
 class List_ListItem extends external_React_default.a.PureComponent {
   // TODO performance: get feeds to send appropriately sized images rather
   // than waiting longer and scaling down on client?
   constructor(props) {
     super(props);
@@ -7565,17 +7663,25 @@ class List_ListItem extends external_Rea
           )
         ),
         external_React_default.a.createElement("div", { className: "ds-list-image", style: { backgroundImage: `url(${this.props.image_src})` } }),
         external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
           campaignId: this.props.campaignId,
           rows: [{ id: this.props.id, pos: this.props.pos }],
           dispatch: this.props.dispatch,
           source: this.props.type })
-      )
+      ),
+      external_React_default.a.createElement(DSLinkMenu, {
+        index: this.props.index,
+        dispatch: this.props.dispatch,
+        intl: this.props.intl,
+        url: this.props.url,
+        title: this.props.title,
+        source: this.props.source,
+        type: this.props.type })
     );
   }
 }
 
 /**
  * @note exported for testing only
  */
 function _List(props) {
@@ -7626,16 +7732,17 @@ const List = Object(external_ReactRedux_
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
 
 
 
 
 
 
 
+
 class Hero_Hero extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
     this.onLinkClick = this.onLinkClick.bind(this);
   }
 
   onLinkClick(event) {
     if (this.props.dispatch) {
@@ -7692,59 +7799,71 @@ class Hero_Hero extends external_React_d
         "div",
         { className: "ds-header" },
         this.props.title
       ),
       external_React_default.a.createElement(
         "div",
         { className: `ds-hero ds-hero-${this.props.border}` },
         external_React_default.a.createElement(
-          SafeAnchor_SafeAnchor,
-          {
-            className: "wrapper",
-            dispatch: this.props.dispatch,
-            onLinkClick: this.onLinkClick,
-            url: heroRec.url },
+          "div",
+          { className: "ds-hero-item" },
           external_React_default.a.createElement(
-            "div",
-            { className: "img-wrapper" },
-            external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${heroRec.image_src})` } })
-          ),
-          external_React_default.a.createElement(
-            "div",
-            { className: "meta" },
+            SafeAnchor_SafeAnchor,
+            {
+              className: "wrapper",
+              dispatch: this.props.dispatch,
+              onLinkClick: this.onLinkClick,
+              url: heroRec.url },
+            external_React_default.a.createElement(
+              "div",
+              { className: "img-wrapper" },
+              external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${heroRec.image_src})` } })
+            ),
             external_React_default.a.createElement(
               "div",
-              { className: "header-and-excerpt" },
+              { className: "meta" },
               external_React_default.a.createElement(
-                "header",
-                null,
-                heroRec.title
+                "div",
+                { className: "header-and-excerpt" },
+                external_React_default.a.createElement(
+                  "header",
+                  null,
+                  heroRec.title
+                ),
+                external_React_default.a.createElement(
+                  "p",
+                  { className: "excerpt" },
+                  heroRec.excerpt
+                )
               ),
-              external_React_default.a.createElement(
+              heroRec.context ? external_React_default.a.createElement(
                 "p",
-                { className: "excerpt" },
-                heroRec.excerpt
+                { className: "context" },
+                heroRec.context
+              ) : external_React_default.a.createElement(
+                "p",
+                { className: "source" },
+                heroRec.domain
               )
             ),
-            heroRec.context ? external_React_default.a.createElement(
-              "p",
-              { className: "context" },
-              heroRec.context
-            ) : external_React_default.a.createElement(
-              "p",
-              { className: "source" },
-              heroRec.domain
-            )
+            external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
+              campaignId: heroRec.campaignId,
+              rows: [{ id: heroRec.id, pos: heroRec.pos }],
+              dispatch: this.props.dispatch,
+              source: this.props.type })
           ),
-          external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
-            campaignId: heroRec.campaignId,
-            rows: [{ id: heroRec.id, pos: heroRec.pos }],
+          external_React_default.a.createElement(DSLinkMenu, {
+            index: this.props.index,
             dispatch: this.props.dispatch,
-            source: this.props.type })
+            intl: this.props.intl,
+            url: heroRec.url,
+            title: heroRec.title,
+            source: heroRec.domain,
+            type: this.props.type })
         ),
         external_React_default.a.createElement(
           "div",
           { className: `${this.props.subComponentType}` },
           this.props.subComponentType === `cards` ? cards : list
         )
       )
     );
@@ -8017,17 +8136,17 @@ function layoutRender(layout, feeds, spo
         data.recommendations[i].pos = positions[component.type]++;
       }
 
       return Object.assign({}, component, { data });
     })
   }));
 });
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSites.jsx
-var TopSites = __webpack_require__(31);
+var TopSites = __webpack_require__(34);
 
 // CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopSites/TopSites.jsx
 
 
 
 
 class TopSites_TopSites extends external_React_default.a.PureComponent {
   render() {
@@ -12318,20 +12437,20 @@ const cardContextTypes = {
     intlID: "type_label_downloaded",
     icon: "download"
   }
 };
 // EXTERNAL MODULE: external "ReactRedux"
 var external_ReactRedux_ = __webpack_require__(24);
 
 // EXTERNAL MODULE: ./content-src/lib/link-menu-options.js
-var link_menu_options = __webpack_require__(43);
+var link_menu_options = __webpack_require__(32);
 
 // EXTERNAL MODULE: ./content-src/components/LinkMenu/LinkMenu.jsx
-var LinkMenu = __webpack_require__(42);
+var LinkMenu = __webpack_require__(30);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(10);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: ./content-src/lib/screenshot-utils.js
 var screenshot_utils = __webpack_require__(44);
 
@@ -12656,17 +12775,17 @@ var Actions = __webpack_require__(2);
 // EXTERNAL MODULE: external "ReactIntl"
 var external_ReactIntl_ = __webpack_require__(5);
 
 // EXTERNAL MODULE: external "React"
 var external_React_ = __webpack_require__(10);
 var external_React_default = /*#__PURE__*/__webpack_require__.n(external_React_);
 
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSitesConstants.js
-var TopSitesConstants = __webpack_require__(32);
+var TopSitesConstants = __webpack_require__(35);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteFormInput.jsx
 
 
 
 class TopSiteFormInput_TopSiteFormInput extends external_React_default.a.PureComponent {
   constructor(props) {
     super(props);
@@ -12734,17 +12853,17 @@ class TopSiteFormInput_TopSiteFormInput 
 }
 
 TopSiteFormInput_TopSiteFormInput.defaultProps = {
   showClearButton: false,
   value: "",
   validationError: false
 };
 // EXTERNAL MODULE: ./content-src/components/TopSites/TopSite.jsx
-var TopSite = __webpack_require__(41);
+var TopSite = __webpack_require__(43);
 
 // CONCATENATED MODULE: ./content-src/components/TopSites/TopSiteForm.jsx
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TopSiteForm", function() { return TopSiteForm_TopSiteForm; });
 
 
 
 
 
--- a/browser/components/newtab/docs/v2-system-addon/data_dictionary.md
+++ b/browser/components/newtab/docs/v2-system-addon/data_dictionary.md
@@ -262,10 +262,12 @@ This encoding mapping was defined in `sy
 | Preference | Encoded value |
 | --- | --- |
 | `showSearch` | 1 |
 | `showTopSites` | 2 |
 | `showTopStories` | 4 |
 | `showHighlights` | 8 |
 | `showSnippets`   | 16 |
 | `showSponsored`  | 32 |
+| `showCFRAddons`  | 64 |
+| `showCFRFeatures` | 128 |
 
 Each item above could be combined with other items through bitwise OR operation
--- a/browser/components/newtab/docs/v2-system-addon/data_events.md
+++ b/browser/components/newtab/docs/v2-system-addon/data_events.md
@@ -944,33 +944,33 @@ This reports the user's interaction with
   "client_id": "26288a14-5cc4-d14f-ae0a-bb01ef45be9c",
   "action": "cfr_user_event",
   "addon_version": "20180710100040",
   "impression_id": "n/a",
   "locale": "en-US",
   "source": "CFR",
   // message_id could be the ID of the recommendation, such as "wikipedia_addon"
   "message_id": "wikipedia_addon",
-  "event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
+  "event": "[INSTALL | PIN | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
 }
 ```
 
 #### CFR interaction pings for release channel
 ```js
 {
   "client_id": "n/a",
   "action": "cfr_user_event",
   "addon_version": "20180710100040",
   "impression_id": "{005deed0-e3e4-4c02-a041-17405fd703f6}",
   "locale": "en-US",
   "source": "CFR",
   // message_id should be a bucket ID in the release channel, we may not use the
   // individual ID, such as addon ID, per legal's request
   "message_id": "bucket_id",
-  "event": "[INSTALL | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
+  "event": "[INSTALL | PIN | BLOCK | DISMISS | RATIONALE | LEARN_MORE | CLICK_DOORHANGER | MANAGE]"
 }
 ```
 
 ### Targeting error pings
 
 This reports when an error has occurred when parsing/evaluating a JEXL targeting string in a message.
 
 ```js
--- a/browser/components/newtab/lib/ASRouter.jsm
+++ b/browser/components/newtab/lib/ASRouter.jsm
@@ -414,17 +414,17 @@ class _ASRouter {
   async loadMessagesFromAllProviders() {
     const needsUpdate = this.state.providers.filter(provider => MessageLoaderUtils.shouldProviderUpdate(provider));
     // Don't do extra work if we don't need any updates
     if (needsUpdate.length) {
       let newState = {messages: [], providers: []};
       for (const provider of this.state.providers) {
         if (needsUpdate.includes(provider)) {
           let {messages, lastUpdated} = await MessageLoaderUtils.loadMessagesForProvider(provider, this._storage);
-          messages = messages.filter(({category}) => !category || ASRouterPreferences.getUserPreference(category));
+          messages = messages.filter(({content}) => !content || !content.category || ASRouterPreferences.getUserPreference(content.category));
           newState.providers.push({...provider, lastUpdated});
           newState.messages = [...newState.messages, ...messages];
         } else {
           // Skip updating this provider's messages if no update is required
           let messages = this.state.messages.filter(msg => msg.provider === provider.id);
           newState.providers.push(provider);
           newState.messages = [...newState.messages, ...messages];
         }
--- a/browser/components/newtab/lib/CFRMessageProvider.jsm
+++ b/browser/components/newtab/lib/CFRMessageProvider.jsm
@@ -32,24 +32,31 @@ const WIKIPEDIA_CONTEXT_MENU_SEARCH_PARA
   min_frecency: 10000,
 };
 const REDDIT_ENHANCEMENT_PARAMS = {
   existing_addons: ["jid1-xUfzOsOFlzSOXg@jetpack"],
   open_urls: ["www.reddit.com", "reddit.com"],
   sumo_path: "extensionrecommendations",
   min_frecency: 10000,
 };
-const PINNED_TABS_TARGET_SITES = ["trello.com", "www.trello.com", "wunderlist.com", "www.wunderlist.com", "docs.google.com", "www.docs.google.com", "calendar.google.com", "www.calendar.google.com", "simplenote.com", "www.simplenote.com", "airtable.com", "www.airtable.com", "todoist.com", "www.todoist.com", "slack.com", "www.slack.com", "irccloud.com", "www.irccloud.com", "products.office.com", "www.products.office.com", "messenger.com", "www.messenger.com", "discordapp.com", "www.discordapp.com", "web.wechat.com", "www.web.wechat.com", "web.whatsapp.com", "www.web.whatsapp.com", "gmail.com", "www.gmail.com", "mail.yahoo.com", "www.mail.yahoo.com", "outlook.com", "www.outlook.com", "polymail.io", "www.polymail.io", "icloud.com", "www.icloud.com", "mail.aol.com", "www.mail.aol.com", "lightroom.adobe.com", "www.lightroom.adobe.com", "facebook.com", "www.facebook.com", "twitter.com", "www.twitter.com", "instagram.com", "www.instagram.com", "pinterest.com", "www.pinterest.com", "reddit.com", "www.reddit.com", "coursera.org", "www.coursera.org", "edx.org", "www.edx.org", "udemy.com", "www.udemy.com", "skillshare.com", "www.skillshare.com", "pluralsight.com", "www.pluralsight.com", "udacity.com", "www.udacity.com", "tumblr.com", "www.tumblr.com", "quora.com", "www.quora.com", "deviantart.com", "www.deviantart.com", "github.com", "www.github.com", "kaggle.com", "www.kaggle.com", "dropbox.com", "www.dropbox.com", "drive.google.com", "www.drive.google.com", "box.com", "www.box.com", "netflix.com", "www.netflix.com", "primevideo.com", "www.primevideo.com", "hulu.com", "www.hulu.com", "crave.ca", "www.crave.ca", "twitch.tv", "www.twitch.tv", "youtube.com", "www.youtube.com", "craigslist.org", "www.craigslist.org", "kijiji.ca", "www.kijiji.ca"];
+const PINNED_TABS_TARGET_SITES = [
+  "docs.google.com", "www.docs.google.com", "calendar.google.com",
+  "messenger.com", "www.messenger.com", "web.whatsapp.com", "mail.google.com",
+  "outlook.live.com", "facebook.com", "www.facebook.com", "twitter.com", "www.twitter.com",
+  "reddit.com", "www.reddit.com", "github.com", "www.github.com", "youtube.com", "www.youtube.com",
+  "feedly.com", "www.feedly.com", "drive.google.com", "amazon.com", "www.amazon.com",
+  "messages.android.com",
+];
 
 const CFR_MESSAGES = [
   {
     id: "FACEBOOK_CONTAINER_3",
     template: "cfr_doorhanger",
-    category: "cfrAddons",
     content: {
+      category: "cfrAddons",
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: FACEBOOK_CONTAINER_PARAMS.sumo_path,
       },
       addon: {
@@ -90,18 +97,18 @@ const CFR_MESSAGES = [
       (xpinstallEnabled == true) &&
       (${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
       (${JSON.stringify(FACEBOOK_CONTAINER_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${FACEBOOK_CONTAINER_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
     trigger: {id: "openURL", params: FACEBOOK_CONTAINER_PARAMS.open_urls},
   },
   {
     id: "GOOGLE_TRANSLATE_3",
     template: "cfr_doorhanger",
-    category: "cfrAddons",
     content: {
+      category: "cfrAddons",
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: GOOGLE_TRANSLATE_PARAMS.sumo_path,
       },
       addon: {
@@ -142,18 +149,18 @@ const CFR_MESSAGES = [
       (xpinstallEnabled == true) &&
       (${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
       (${JSON.stringify(GOOGLE_TRANSLATE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${GOOGLE_TRANSLATE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
     trigger: {id: "openURL", params: GOOGLE_TRANSLATE_PARAMS.open_urls},
   },
   {
     id: "YOUTUBE_ENHANCE_3",
     template: "cfr_doorhanger",
-    category: "cfrAddons",
     content: {
+      category: "cfrAddons",
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: YOUTUBE_ENHANCE_PARAMS.sumo_path,
       },
       addon: {
@@ -194,19 +201,19 @@ const CFR_MESSAGES = [
       (xpinstallEnabled == true) &&
       (${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
       (${JSON.stringify(YOUTUBE_ENHANCE_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${YOUTUBE_ENHANCE_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
     trigger: {id: "openURL", params: YOUTUBE_ENHANCE_PARAMS.open_urls},
   },
   {
     id: "WIKIPEDIA_CONTEXT_MENU_SEARCH_3",
     template: "cfr_doorhanger",
-    category: "cfrAddons",
     exclude: true,
     content: {
+      category: "cfrAddons",
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.sumo_path,
       },
       addon: {
@@ -247,19 +254,19 @@ const CFR_MESSAGES = [
       (xpinstallEnabled == true) &&
       (${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
       (${JSON.stringify(WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
     trigger: {id: "openURL", params: WIKIPEDIA_CONTEXT_MENU_SEARCH_PARAMS.open_urls},
   },
   {
     id: "REDDIT_ENHANCEMENT_3",
     template: "cfr_doorhanger",
-    category: "cfrAddons",
     exclude: true,
     content: {
+      category: "cfrAddons",
       bucket_id: "CFR_M1",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-extension-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
       },
       addon: {
@@ -300,27 +307,33 @@ const CFR_MESSAGES = [
       (xpinstallEnabled == true) &&
       (${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.existing_addons)} intersect addonsInfo.addons|keys)|length == 0 &&
       (${JSON.stringify(REDDIT_ENHANCEMENT_PARAMS.open_urls)} intersect topFrecentSites[.frecency >= ${REDDIT_ENHANCEMENT_PARAMS.min_frecency}]|mapToProperty('host'))|length > 0`,
     trigger: {id: "openURL", params: REDDIT_ENHANCEMENT_PARAMS.open_urls},
   },
   {
     id: "PIN_TAB",
     template: "cfr_doorhanger",
-    category: "cfrFeatures",
-    exclude: true,
     content: {
+      category: "cfrFeatures",
       bucket_id: "CFR_PIN_TAB",
       notification_text: {string_id: "cfr-doorhanger-extension-notification"},
       heading_text: {string_id: "cfr-doorhanger-pintab-heading"},
       info_icon: {
         label: {string_id: "cfr-doorhanger-extension-sumo-link"},
         sumo_path: REDDIT_ENHANCEMENT_PARAMS.sumo_path,
       },
-      text: "Get easy access to your most-used sites. Keep sites open in a tab (even when you restart).",
+      text: {string_id: "cfr-doorhanger-pintab-description"},
+      descriptionDetails: {
+        steps: [
+          {"string_id": "cfr-doorhanger-pintab-step1"},
+          {"string_id": "cfr-doorhanger-pintab-step2"},
+          {"string_id": "cfr-doorhanger-pintab-step3"},
+        ],
+      },
       buttons: {
         primary: {
           label: {string_id: "cfr-doorhanger-pintab-ok-button"},
           action: {
             type: "PIN_CURRENT_TAB",
           },
         },
         secondary: [{
@@ -332,17 +345,18 @@ const CFR_MESSAGES = [
           label: {string_id: "cfr-doorhanger-extension-manage-settings-button"},
           action: {
             type: "OPEN_PREFERENCES_PAGE",
             data: {category: "general-cfrfeatures", origin: "CFR"},
           },
         }],
       },
     },
-    targeting: `!hasPinnedTabs && recentVisits[.timestamp > (currentDate|date - 3600 * 1000 * 1)]|length >= 1`,
+    targeting: `localeLanguageCode == "en" && !hasPinnedTabs && recentVisits[.timestamp > (currentDate|date - 3600 * 1000 * 1)]|length >= 3`,
+    frequency: {lifetime: 3},
     trigger: {id: "frequentVisits", params: PINNED_TABS_TARGET_SITES},
   },
 ];
 
 const CFRMessageProvider = {
   getMessages() {
     return CFR_MESSAGES.filter(msg => !msg.exclude);
   },
--- a/browser/components/newtab/lib/CFRPageActions.jsm
+++ b/browser/components/newtab/lib/CFRPageActions.jsm
@@ -1,26 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-const {Localization} = ChromeUtils.import("resource://gre/modules/Localization.jsm");
+const {DOMLocalization} = ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 const POPUP_NOTIFICATION_ID = "contextual-feature-recommendation";
+const PAUSE_BUTTON_ID = "cfr-notification-footer-pause-button";
 const SUMO_BASE_URL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 const ADDONS_API_URL = "https://services.addons.mozilla.org/api/v3/addons/addon";
+const ANIMATIONS_ENABLED_PREF = "toolkit.cosmeticAnimations.enabled";
 
 const DELAY_BEFORE_EXPAND_MS = 1000;
+const CATEGORY_ICONS = {
+  "cfrAddons": "webextensions-icon",
+  "cfrFeatures": "recommendations-icon",
+};
 
 /**
  * A WeakMap from browsers to {host, recommendation} pairs. Recommendations are
  * defined in the ExtensionDoorhanger.schema.json.
  *
  * A recommendation is specific to a browser and host and is active until the
  * given browser is closed or the user navigates (within that browser) away from
  * the host.
@@ -47,28 +53,29 @@ class PageAction {
     // Please use dispatchUserAction instead.
     this._dispatchToASRouter = dispatchToASRouter;
 
     this._popupStateChange = this._popupStateChange.bind(this);
     this._collapse = this._collapse.bind(this);
     this._showPopupOnClick = this._showPopupOnClick.bind(this);
     this.dispatchUserAction = this.dispatchUserAction.bind(this);
 
-    this._l10n = new Localization([
+    this._l10n = new DOMLocalization([
       "browser/newtab/asrouter.ftl",
     ]);
 
     // Saved timeout IDs for scheduled state changes, so they can be cancelled
     this.stateTransitionTimeoutIDs = [];
   }
 
   async showAddressBarNotifier(recommendation, shouldExpand = false) {
     this.container.hidden = false;
 
     this.label.value = await this.getStrings(recommendation.content.notification_text);
+    this.button.setAttribute("data-cfr-icon", CATEGORY_ICONS[recommendation.content.category]);
 
     // Wait for layout to flush to avoid a synchronous reflow then calculate the
     // label width. We can safely get the width even though the recommendation is
     // collapsed; the label itself remains full width (with its overflow hidden)
     let [{width}] = await this.window.promiseDocumentFlushed(() => this.label.getClientRects());
     this.urlbar.style.setProperty("--cfr-label-width", `${width}px`);
 
     this.container.addEventListener("click", this._showPopupOnClick);
@@ -126,16 +133,23 @@ class PageAction {
       }, delay));
     } else {
       // Non-delayed state change overrides any scheduled state changes
       this._clearScheduledStateChanges();
       if (this.urlbar.getAttribute("cfr-recommendation-state") === "expanded") {
         this.urlbar.setAttribute("cfr-recommendation-state", "collapsed");
       }
     }
+
+    // TODO: FIXME: find a nicer way of cleaning this up. Maybe listening to "popuphidden"?
+    // Remove click listener on pause button;
+    if (this.onPauseClick) {
+      this.window.document.getElementById(PAUSE_BUTTON_ID).removeEventListener("click", this.onPauseClick);
+      delete this.onPauseClick;
+    }
   }
 
   _clearScheduledStateChanges() {
     while (this.stateTransitionTimeoutIDs.length > 0) {
       // clearTimeout is safe even with invalid/expired IDs
       this.window.clearTimeout(this.stateTransitionTimeoutIDs.pop());
     }
   }
@@ -216,43 +230,28 @@ class PageAction {
         return acc;
       }, {});
       mainString.attributes = attributes;
     }
 
     return subAttribute ? mainString.attributes[subAttribute] : mainString;
   }
 
-  async _renderPopup(message, browser) { // eslint-disable-line max-statements
-    const {id, content} = message;
-
-    const headerLabel = this.window.document.getElementById("cfr-notification-header-label");
-    const headerLink = this.window.document.getElementById("cfr-notification-header-link");
-    const headerImage = this.window.document.getElementById("cfr-notification-header-image");
+  async _setAddonAuthorAndRating(document, content) {
     const author = this.window.document.getElementById("cfr-notification-author");
-    const footerText = this.window.document.getElementById("cfr-notification-footer-text");
     const footerFilledStars = this.window.document.getElementById("cfr-notification-footer-filled-stars");
     const footerEmptyStars = this.window.document.getElementById("cfr-notification-footer-empty-stars");
     const footerUsers = this.window.document.getElementById("cfr-notification-footer-users");
     const footerSpacer = this.window.document.getElementById("cfr-notification-footer-spacer");
-    const footerLink = this.window.document.getElementById("cfr-notification-footer-learn-more-link");
-
-    headerLabel.value = await this.getStrings(content.heading_text);
-    headerLink.setAttribute("href", SUMO_BASE_URL + content.info_icon.sumo_path);
-    headerLink.setAttribute(this.window.RTL_UI ? "left" : "right", 0);
-    headerImage.setAttribute("tooltiptext", await this.getStrings(content.info_icon.label, "tooltiptext"));
-    headerLink.onclick = () => this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "RATIONALE"});
 
     author.textContent = await this.getStrings({
       string_id: "cfr-doorhanger-extension-author",
       args: {name: content.addon.author},
     });
 
-    footerText.textContent = await this.getStrings(content.text);
-
     const {rating} = content.addon;
     if (rating) {
       const MAX_RATING = 5;
       const STARS_WIDTH = 17 * MAX_RATING;
       const calcWidth = stars => `${stars / MAX_RATING * STARS_WIDTH}px`;
       footerFilledStars.style.width = calcWidth(rating);
       footerEmptyStars.style.width = calcWidth(MAX_RATING - rating);
 
@@ -283,44 +282,149 @@ class PageAction {
     }
 
     // Spacer pushes the link to the opposite end when there's other content
     if (rating || users) {
       footerSpacer.removeAttribute("hidden");
     } else {
       footerSpacer.setAttribute("hidden", true);
     }
+  }
 
-    footerLink.value = await this.getStrings({string_id: "cfr-doorhanger-extension-learn-more-link"});
-    footerLink.setAttribute("href", content.addon.amo_url);
-    footerLink.onclick = () => this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "LEARN_MORE"});
+  _createElementAndAppend({type, id}, parent) {
+    let element = this.window.document.createElement(type);
+    if (id) {
+      element.setAttribute("id", id);
+    }
+    parent.appendChild(element);
+    return element;
+  }
+
+  async _renderPinTabAnimation() {
+    const ANIMATION_CONTAINER_ID = "cfr-notification-footer-pintab-animation-container";
+    const footer = this.window.document.getElementById("cfr-notification-footer");
+    let animationContainer = this.window.document.getElementById(ANIMATION_CONTAINER_ID);
+    if (!animationContainer) {
+      animationContainer = this._createElementAndAppend({type: "vbox", id: ANIMATION_CONTAINER_ID}, footer);
+
+      // spacer
+      this._createElementAndAppend("vbox", animationContainer).setAttribute("flex", 1);
+
+      let controlsContainer = this._createElementAndAppend(
+        {type: "hbox", id: "cfr-notification-footer-animation-controls"}, animationContainer);
+
+      // spacer
+      this._createElementAndAppend({type: "vbox"}, controlsContainer).setAttribute("flex", 1);
+
+      let pauseButton = this._createElementAndAppend({type: "hbox", id: PAUSE_BUTTON_ID}, controlsContainer);
+
+      let pauseLabel = this._createElementAndAppend(
+        {type: "label", id: "cfr-notification-footer-pause-label"}, pauseButton);
+      pauseLabel.textContent = await this.getStrings({"string_id": "cfr-doorhanger-pintab-animation-pause"});
+
+      // pause icon
+      this._createElementAndAppend({type: "image", id: "cfr-notification-footer-pause-icon"}, pauseButton);
+    }
+
+    animationContainer.toggleAttribute("animate", Services.prefs.getBoolPref(ANIMATIONS_ENABLED_PREF, true));
+    animationContainer.removeAttribute("paused");
+
+    if (!this.onPauseClick) {
+      let pauseButton = this.window.document.getElementById(PAUSE_BUTTON_ID);
+      this.onPauseClick = () => { animationContainer.setAttribute("paused", true); };
+      pauseButton.addEventListener("click", this.onPauseClick);
+    }
+  }
+
+  async _renderPopup(message, browser) {
+    const {id, content} = message;
+
+    const headerLabel = this.window.document.getElementById("cfr-notification-header-label");
+    const headerLink = this.window.document.getElementById("cfr-notification-header-link");
+    const headerImage = this.window.document.getElementById("cfr-notification-header-image");
+    const footerText = this.window.document.getElementById("cfr-notification-footer-text");
+    const footerLink = this.window.document.getElementById("cfr-notification-footer-learn-more-link");
+    const {primary, secondary} = content.buttons;
+    let primaryActionCallback;
+    let options = {};
+    let panelTitle;
 
-    const {primary, secondary} = content.buttons;
+    // Use the message category as a CSS selector to hide different parts of the
+    // notification template markup
+    this.window.document.getElementById("contextual-feature-recommendation-notification")
+      .setAttribute("data-notification-category", message.content.category);
+
+    headerLabel.value = await this.getStrings(content.heading_text);
+    headerLink.setAttribute("href", SUMO_BASE_URL + content.info_icon.sumo_path);
+    headerLink.setAttribute(this.window.RTL_UI ? "left" : "right", 0);
+    headerImage.setAttribute("tooltiptext", await this.getStrings(content.info_icon.label, "tooltiptext"));
+    headerLink.onclick = () => this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "RATIONALE"});
+
+    footerText.textContent = await this.getStrings(content.text);
+
+    if (content.addon) {
+      await this._setAddonAuthorAndRating(this.window.document, content);
+      panelTitle = await this.getStrings(content.addon.title);
+      options = {popupIconURL: content.addon.icon};
+
+      footerLink.value = await this.getStrings({string_id: "cfr-doorhanger-extension-learn-more-link"});
+      footerLink.setAttribute("href", content.addon.amo_url);
+      footerLink.onclick = () => this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "LEARN_MORE"});
+
+      primaryActionCallback = async () => {
+        primary.action.data.url = await CFRPageActions._fetchLatestAddonVersion(content.addon.id); // eslint-disable-line no-use-before-define
+        this._blockMessage(id);
+        this.dispatchUserAction(primary.action);
+        this.hideAddressBarNotifier();
+        this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "INSTALL"});
+        RecommendationMap.delete(browser);
+      };
+    } else {
+      const stepsContainerId = "cfr-notification-feature-steps";
+      let stepsContainer = this.window.document.getElementById(stepsContainerId);
+      primaryActionCallback = () => {
+        this._blockMessage(id);
+        this.dispatchUserAction(primary.action);
+        this.hideAddressBarNotifier();
+        this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "PIN"});
+        RecommendationMap.delete(browser);
+      };
+      panelTitle = await this.getStrings(content.heading_text);
+
+      if (stepsContainer) { // If it exists we need to empty it
+        stepsContainer.remove();
+        stepsContainer = stepsContainer.cloneNode(false);
+      } else {
+        stepsContainer = this.window.document.createElement("vbox");
+        stepsContainer.setAttribute("id", stepsContainerId);
+      }
+      footerText.parentNode.appendChild(stepsContainer);
+      for (let step of content.descriptionDetails.steps) {
+        const li = this.window.document.createElement("li");
+        this._l10n.setAttributes(li, step.string_id);
+        stepsContainer.appendChild(li);
+      }
+      await this._l10n.translateElements([...stepsContainer.children]);
+
+      await this._renderPinTabAnimation();
+    }
+
     const primaryBtnStrings = await this.getStrings(primary.label);
+    const mainAction = {
+      label: primaryBtnStrings,
+      accessKey: primaryBtnStrings.attributes.accesskey,
+      callback: primaryActionCallback,
+    };
 
     // For each secondary action, get the strings and attributes
     const secondaryBtnStrings = [];
     for (let button of secondary) {
       let label = await this.getStrings(button.label);
       secondaryBtnStrings.push({label, attributes: label.attributes});
     }
-
-    const mainAction = {
-      label: primaryBtnStrings,
-      accessKey: primaryBtnStrings.attributes.accesskey,
-      callback: async () => {
-        primary.action.data.url = await CFRPageActions._fetchLatestAddonVersion(content.addon.id); // eslint-disable-line no-use-before-define
-        this._blockMessage(id);
-        this.dispatchUserAction(primary.action);
-        this.hideAddressBarNotifier();
-        this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "INSTALL"});
-        RecommendationMap.delete(browser);
-      },
-    };
-
     const secondaryActions = [{
       label: secondaryBtnStrings[0].label,
       accessKey: secondaryBtnStrings[0].attributes.accesskey,
       callback: () => {
         this.dispatchUserAction(secondary[0].action);
         this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "DISMISS"});
       },
     }, {
@@ -336,31 +440,29 @@ class PageAction {
       label: secondaryBtnStrings[2].label,
       accessKey: secondaryBtnStrings[2].attributes.accesskey,
       callback: () => {
         this.dispatchUserAction(secondary[2].action);
         this._sendTelemetry({message_id: id, bucket_id: content.bucket_id, event: "MANAGE"});
       },
     }];
 
-    const options = {
-      popupIconURL: content.addon.icon,
-      hideClose: true,
-      eventCallback: this._popupStateChange,
-    };
-
     // Actually show the notification
     this.currentNotification = this.window.PopupNotifications.show(
       browser,
       POPUP_NOTIFICATION_ID,
-      await this.getStrings(content.addon.title),
+      panelTitle,
       "cfr",
       mainAction,
       secondaryActions,
-      options
+      {
+        ...options,
+        hideClose: true,
+        eventCallback: this._popupStateChange,
+      }
     );
   }
 
   /**
    * Respond to a user click on the recommendation by showing a doorhanger/
    * popup notification
    */
   async _showPopupOnClick(event) {
--- a/browser/components/newtab/lib/DownloadsManager.jsm
+++ b/browser/components/newtab/lib/DownloadsManager.jsm
@@ -1,9 +1,8 @@
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
@@ -37,17 +36,17 @@ this.DownloadsManager = class DownloadsM
                    DownloadsCommon.strings.sizeUnknown,
       referrer: download.source.referrer,
       date_added: download.endTime,
     };
   }
 
   init(store) {
     this._store = store;
-    this._downloadData = DownloadsCommon.getData(null /* null for non-private downloads */, 
+    this._downloadData = DownloadsCommon.getData(null /* null for non-private downloads */,
                                                  true, false, true);
     this._downloadData.addView(this);
   }
 
   onDownloadAdded(download) {
     if (!this._downloadItems.has(download.source.url)) {
       this._downloadItems.set(download.source.url, download);
 
--- a/browser/components/newtab/lib/TelemetryFeed.jsm
+++ b/browser/components/newtab/lib/TelemetryFeed.jsm
@@ -44,16 +44,18 @@ const TAB_PINNED_EVENT = "TabPinned";
 // This is a mapping table between the user preferences and its encoding code
 const USER_PREFS_ENCODING = {
   "showSearch": 1 << 0,
   "feeds.topsites": 1 << 1,
   "feeds.section.topstories": 1 << 2,
   "feeds.section.highlights": 1 << 3,
   "feeds.snippets": 1 << 4,
   "showSponsored": 1 << 5,
+  "asrouter.userprefs.cfr.addons": 1 << 6,
+  "asrouter.userprefs.cfr.features": 1 << 7,
 };
 
 const PREF_IMPRESSION_ID = "impressionId";
 const TELEMETRY_PREF = "telemetry";
 const EVENTS_TELEMETRY_PREF = "telemetry.ut.events";
 const STRUCTURED_INGESTION_TELEMETRY_PREF = "telemetry.structuredIngestion";
 const STRUCTURED_INGESTION_ENDPOINT_PREF = "telemetry.structuredIngestion.endpoint";
 
--- a/browser/components/newtab/locales-src/id/strings.properties
+++ b/browser/components/newtab/locales-src/id/strings.properties
@@ -86,16 +86,19 @@ section_disclaimer_topstories_buttontext
 
 # LOCALIZATION NOTE (prefs_*, settings_*): These are shown in about:preferences
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Konten Beranda Firefox
 prefs_home_description=Pilih konten yang ingin Anda tampilkan dalam Beranda Firefox.
+
+prefs_content_discovery_header=Beranda Firefox
+
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} baris
 prefs_search_header=Pencarian Web
 prefs_topsites_description=Situs yang sering Anda kunjungi
 prefs_topstories_description2=Konten bermutu dari seluruh web, khusus untuk Anda
 prefs_topstories_options_sponsored_label=Konten Sponsor
@@ -139,17 +142,16 @@ topsites_form_image_validation=Gambar ga
 
 # LOCALIZATION NOTE (pocket_read_more): This is shown at the bottom of the
 # trending stories section and precedes a list of links to popular topics.
 pocket_read_more=Topik Populer:
 # LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
 # end of the list of popular topic links.
 pocket_read_even_more=Lihat Cerita Lainnya
 pocket_more_reccommendations=Rekomendasi Lainnya
-pocket_learn_more=Pelajari Lebih Lanjut
 pocket_how_it_works=Panduan
 pocket_cta_button=Dapatkan Pocket
 pocket_cta_text=Simpan cerita yang anda sukai di Pocket, dan dapatkan bacaan menarik untuk Anda.
 
 highlights_empty_state=Mulai menjelajah, dan kami akan menampilkan beberapa artikel bagus, video, dan halaman lain yang baru saja Anda kunjungi atau termarkah di sini.
 # LOCALIZATION NOTE (topstories_empty_state): When there are no recommendations,
 # in the space that would have shown a few stories, this is shown instead.
 # {provider} is replaced by the name of the content provider for this section.
--- a/browser/components/newtab/locales-src/nb-NO/strings.properties
+++ b/browser/components/newtab/locales-src/nb-NO/strings.properties
@@ -88,16 +88,18 @@ section_disclaimer_topstories_buttontext=OK, jeg forstår det
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Innhold Firefox-startside
 prefs_home_description=Velg hvilket innhold som du vil ha på din Firefox-startside.
 
 prefs_content_discovery_header=Firefox startside
+prefs_content_discovery_description=Innholdsoppdagelse på Firefox startside lar deg oppdage relevante artikler av høy kvalitet fra nettet.
+prefs_content_discovery_button=Slå av innholdsoppdagelse
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} rekke;{num} rekker
 prefs_search_header=Nettsøk
 prefs_topsites_description=Mest besøkte nettsteder
 prefs_topstories_description2=Bra innhold fra hele nettet, tilpasset for deg
--- a/browser/components/newtab/locales-src/nn-NO/strings.properties
+++ b/browser/components/newtab/locales-src/nn-NO/strings.properties
@@ -30,17 +30,17 @@ type_label_downloaded=Nedlasta
 # bookmarks"
 menu_action_bookmark=Bokmerke
 menu_action_remove_bookmark=Fjern bokmerke
 menu_action_open_new_window=Opne i nytt vindauge
 menu_action_open_private_window=Opne i eit nytt privat vindauge
 menu_action_dismiss=Avvis
 menu_action_delete=Slett frå historikk
 menu_action_pin=Fest
-menu_action_unpin=L:ys
+menu_action_unpin=Løys
 confirm_history_delete_p1=Er du sikker på at du vil slette alle førekomstar av denne sida frå historikken din?
 # LOCALIZATION NOTE (confirm_history_delete_notice_p2): this string is displayed in
 # the same dialog as confirm_history_delete_p1. "This action" refers to deleting a
 # page from history.
 confirm_history_delete_notice_p2=Denne handlinga kan ikkje angrast.
 menu_action_save_to_pocket=Lagre til Pocket
 menu_action_delete_pocket=Slett frå Pocket
 menu_action_archive_pocket=Arkiver i Pocket
@@ -88,16 +88,17 @@ section_disclaimer_topstories_buttontext=OK, eg forstår det!
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Innhald Firefox-startside
 prefs_home_description=Vel kva for innhald du vil ha på Firefox-startsida di.
 
 prefs_content_discovery_header=Firefox startside
+prefs_content_discovery_description=Innhaldsoppdaging på Firefox startside lèt deg oppdage relevante artiklar av høg kvalitet frå nettet.
 prefs_content_discovery_button=Slå av innhaldsoppdaging
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} rekkje;{num} rekkjer
 prefs_search_header=Nettsøk
 prefs_topsites_description=Sidene du besøkjer mest
--- a/browser/components/newtab/prerendered/locales/id/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/id/activity-stream-strings.js
@@ -35,17 +35,17 @@ window.gActivityStreamStrings = {
   "search_button": "Cari",
   "search_header": "Pencarian {search_engine_name}",
   "search_web_placeholder": "Cari di Web",
   "section_disclaimer_topstories": "Kisah paling menarik di web, dipilih berdasarkan yang Anda baca. Dari Pocket, kini bagian dari Mozilla.",
   "section_disclaimer_topstories_linktext": "Pelajari cara kerjanya.",
   "section_disclaimer_topstories_buttontext": "Oke, paham",
   "prefs_home_header": "Konten Beranda Firefox",
   "prefs_home_description": "Pilih konten yang ingin Anda tampilkan dalam Beranda Firefox.",
-  "prefs_content_discovery_header": "Firefox Home",
+  "prefs_content_discovery_header": "Beranda Firefox",
   "prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
   "prefs_content_discovery_button": "Turn Off Content Discovery",
   "prefs_section_rows_option": "{num} baris",
   "prefs_search_header": "Pencarian Web",
   "prefs_topsites_description": "Situs yang sering Anda kunjungi",
   "prefs_topstories_description2": "Konten bermutu dari seluruh web, khusus untuk Anda",
   "prefs_topstories_options_sponsored_label": "Konten Sponsor",
   "prefs_topstories_sponsored_learn_more": "Pelajari lebih lanjut",
@@ -105,11 +105,10 @@ window.gActivityStreamStrings = {
   "firstrun_form_sub_header": "Lanjutkan ke Firefox Sync",
   "firstrun_email_input_placeholder": "Surel",
   "firstrun_invalid_input": "Surel harus valid",
   "firstrun_extra_legal_links": "Dengan melanjutkan, Anda menyetujui {terms} dan {privacy}.",
   "firstrun_terms_of_service": "Ketentuan Layanan",
   "firstrun_privacy_notice": "Pernyataan Privasi",
   "firstrun_continue_to_login": "Lanjutkan",
   "firstrun_skip_login": "Lewati langkah ini",
-  "context_menu_title": "Buka menu",
-  "pocket_learn_more": "Pelajari Lebih Lanjut"
+  "context_menu_title": "Buka menu"
 };
--- a/browser/components/newtab/prerendered/locales/nb-NO/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/nb-NO/activity-stream-strings.js
@@ -36,18 +36,18 @@ window.gActivityStreamStrings = {
   "search_header": "{search_engine_name}-søk",
   "search_web_placeholder": "Søk på nettet",
   "section_disclaimer_topstories": "De mest interessante nyhetene på nettet, utvalgt basert på hva du leser. Fra Pocket, nå en del av Mozilla.",
   "section_disclaimer_topstories_linktext": "Se hvordan det fungerer.",
   "section_disclaimer_topstories_buttontext": "OK, jeg forstår det",
   "prefs_home_header": "Innhold Firefox-startside",
   "prefs_home_description": "Velg hvilket innhold som du vil ha på din Firefox-startside.",
   "prefs_content_discovery_header": "Firefox startside",
-  "prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
-  "prefs_content_discovery_button": "Turn Off Content Discovery",
+  "prefs_content_discovery_description": "Innholdsoppdagelse på Firefox startside lar deg oppdage relevante artikler av høy kvalitet fra nettet.",
+  "prefs_content_discovery_button": "Slå av innholdsoppdagelse",
   "prefs_section_rows_option": "{num} rekke;{num} rekker",
   "prefs_search_header": "Nettsøk",
   "prefs_topsites_description": "Mest besøkte nettsteder",
   "prefs_topstories_description2": "Bra innhold fra hele nettet, tilpasset for deg",
   "prefs_topstories_options_sponsored_label": "Sponsede historier",
   "prefs_topstories_sponsored_learn_more": "Les mer",
   "prefs_highlights_description": "Et utvalg av nettsteder som du har lagret eller besøkt",
   "prefs_highlights_options_visited_label": "Besøkte nettsider",
--- a/browser/components/newtab/prerendered/locales/nn-NO/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/nn-NO/activity-stream-strings.js
@@ -13,17 +13,17 @@ window.gActivityStreamStrings = {
   "type_label_downloaded": "Nedlasta",
   "menu_action_bookmark": "Bokmerke",
   "menu_action_remove_bookmark": "Fjern bokmerke",
   "menu_action_open_new_window": "Opne i nytt vindauge",
   "menu_action_open_private_window": "Opne i eit nytt privat vindauge",
   "menu_action_dismiss": "Avvis",
   "menu_action_delete": "Slett frå historikk",
   "menu_action_pin": "Fest",
-  "menu_action_unpin": "L:ys",
+  "menu_action_unpin": "Løys",
   "confirm_history_delete_p1": "Er du sikker på at du vil slette alle førekomstar av denne sida frå historikken din?",
   "confirm_history_delete_notice_p2": "Denne handlinga kan ikkje angrast.",
   "menu_action_save_to_pocket": "Lagre til Pocket",
   "menu_action_delete_pocket": "Slett frå Pocket",
   "menu_action_archive_pocket": "Arkiver i Pocket",
   "menu_action_show_file_mac_os": "Vis i Finder",
   "menu_action_show_file_windows": "Opne innhaldsmappe",
   "menu_action_show_file_linux": "Opne innhaldsmappe",
@@ -36,17 +36,17 @@ window.gActivityStreamStrings = {
   "search_header": "{search_engine_name}",
   "search_web_placeholder": "Søk på nettet",
   "section_disclaimer_topstories": "Dei mest interessante historiane på nettet, utvalde basert på kva du les. Frå Pocket, no ein del av Mozilla.",
   "section_disclaimer_topstories_linktext": "Sjå korleis det fungerer.",
   "section_disclaimer_topstories_buttontext": "OK, eg forstår det!",
   "prefs_home_header": "Innhald Firefox-startside",
   "prefs_home_description": "Vel kva for innhald du vil ha på Firefox-startsida di.",
   "prefs_content_discovery_header": "Firefox startside",
-  "prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
+  "prefs_content_discovery_description": "Innhaldsoppdaging på Firefox startside lèt deg oppdage relevante artiklar av høg kvalitet frå nettet.",
   "prefs_content_discovery_button": "Slå av innhaldsoppdaging",
   "prefs_section_rows_option": "{num} rekkje;{num} rekkjer",
   "prefs_search_header": "Nettsøk",
   "prefs_topsites_description": "Sidene du besøkjer mest",
   "prefs_topstories_description2": "Bra innhald frå heile nettet, tilpassa for deg",
   "prefs_topstories_options_sponsored_label": "Sponsa historiar",
   "prefs_topstories_sponsored_learn_more": "Les meir",
   "prefs_highlights_description": "Eit utval av nettsider som du har lagra eller besøkt",
--- a/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
+++ b/browser/components/newtab/test/unit/asrouter/ASRouter.test.js
@@ -962,22 +962,24 @@ describe("ASRouter", () => {
         assert.calledOnce(Router.blockMessageById);
         assert.calledWithExactly(Router.blockMessageById, "RETURN_TO_AMO_1");
       });
     });
 
     describe("#onMessage: PIN_CURRENT_TAB", () => {
       it("should call pin tab with the selectedTab", async () => {
         const msg = fakeExecuteUserAction({type: "PIN_CURRENT_TAB"});
-        const {gBrowser} = msg.target.browser.ownerGlobal;
+        const {gBrowser, ConfirmationHint} = msg.target.browser.ownerGlobal;
 
         await Router.onMessage(msg);
 
         assert.calledOnce(gBrowser.pinTab);
         assert.calledWithExactly(gBrowser.pinTab, gBrowser.selectedTab);
+        assert.calledOnce(ConfirmationHint.show);
+        assert.calledWithExactly(ConfirmationHint.show, gBrowser.selectedTab, "pinTab", {showDescription: true});
       });
     });
 
     describe("#dispatch(action, target)", () => {
       it("should an action and target to onMessage", async () => {
         // use the IMPRESSION action to make sure actions are actually getting processed
         sandbox.stub(Router, "addImpression");
         sandbox.spy(Router, "onMessage");
--- a/browser/components/newtab/test/unit/asrouter/CFRMessageProvider.test.js
+++ b/browser/components/newtab/test/unit/asrouter/CFRMessageProvider.test.js
@@ -6,18 +6,18 @@ const REGULAR_IDS = [
   "GOOGLE_TRANSLATE",
   "YOUTUBE_ENHANCE",
   // These are excluded for now.
   // "WIKIPEDIA_CONTEXT_MENU_SEARCH",
   // "REDDIT_ENHANCEMENT",
 ];
 
 describe("CFRMessageProvider", () => {
-  it("should have a total of 3 messages", () => {
-    assert.lengthOf(messages, 3);
+  it("should have a total of 4 messages", () => {
+    assert.lengthOf(messages, 4);
   });
   it("should have one message each for the three regular addons", () => {
     for (const id of REGULAR_IDS) {
       const cohort3 = messages.find(msg => msg.id === `${id}_3`);
       assert.ok(cohort3, `contains three day cohort for ${id}`);
       assert.deepEqual(cohort3.frequency, {lifetime: 3}, "three day cohort has the right frequency cap");
       assert.notInclude(cohort3.targeting, `providerCohorts.cfr`);
     }
@@ -27,9 +27,14 @@ describe("CFRMessageProvider", () => {
       // Ensure that the CFR messages that are recommending an addon have this targeting.
       // In the future when we can do targeting based on category, this test will change.
       // See bug 1494778 and 1497653
       if (message.id !== "PIN_TAB") {
         assert.include(message.targeting, `(xpinstallEnabled == true)`);
       }
     }
   });
+  it("should restrict all messages to `en` locale for now", () => {
+    for (const message of messages) {
+      assert.include(message.targeting, `localeLanguageCode == "en"`);
+    }
+  });
 });
--- a/browser/components/newtab/test/unit/asrouter/CFRPageActions.test.js
+++ b/browser/components/newtab/test/unit/asrouter/CFRPageActions.test.js
@@ -17,46 +17,56 @@ describe("CFRPageActions", () => {
     "contextual-feature-recommendation",
     "cfr-button",
     "cfr-label",
     "contextual-feature-recommendation-notification",
     "cfr-notification-header-label",
     "cfr-notification-header-link",
     "cfr-notification-header-image",
     "cfr-notification-author",
+    "cfr-notification-footer",
     "cfr-notification-footer-text",
     "cfr-notification-footer-filled-stars",
     "cfr-notification-footer-empty-stars",
     "cfr-notification-footer-users",
     "cfr-notification-footer-spacer",
     "cfr-notification-footer-learn-more-link",
+    "cfr-notification-footer-pintab-animation-container",
+    "cfr-notification-footer-pause-button",
+  ];
+  const elementClassNames = [
+    "popup-notification-body-container",
   ];
 
   beforeEach(() => {
     sandbox = sinon.createSandbox();
     clock = sandbox.useFakeTimers();
 
     fakeRecommendation = {
       id: "fake_id",
       content: {
+        category: "cfrDummy",
         bucket_id: "fake_bucket_id",
         notification_text: "Fake Notification Text",
         info_icon: {
           label: "Fake Info Icon Label",
           sumo_path: "a_help_path_fragment",
         },
         heading_text: "Fake Heading Text",
         addon:  {
           title: "Fake Addon Title",
           author: "Fake Addon Author",
           icon: "a_path_to_some_icon",
           rating: 4.2,
           users: 1234,
           amo_url: "a_path_to_amo",
         },
+        descriptionDetails: {
+          steps: [{string_id: "cfr-features-step1"}],
+        },
         text: "Here is the recommendation text body",
         buttons: {
           primary: {
             label: {string_id: "primary_button_id"},
             action: {
               id: "primary_action",
               data: {},
             },
@@ -81,36 +91,44 @@ describe("CFRPageActions", () => {
         host: fakeHost,
       },
       ownerGlobal: window,
     };
     dispatchStub = sandbox.stub();
 
     globals = new GlobalOverrider();
     globals.set({
-      Localization: class {},
+      DOMLocalization: class {},
       promiseDocumentFlushed: sandbox.stub().callsFake(fn => Promise.resolve(fn())),
       PopupNotifications: {
         show: sandbox.stub(),
         remove: sandbox.stub(),
       },
       PrivateBrowsingUtils: {isWindowPrivate: sandbox.stub().returns(false)},
       gBrowser: {selectedBrowser: fakeBrowser},
     });
 
     elements = {};
     const [body] = document.getElementsByTagName("body");
     containerElem = document.createElement("div");
     body.appendChild(containerElem);
     for (const id of elementIDs) {
       const elem = document.createElement("div");
       elem.setAttribute("id", id);
+      // TODO: Remove this once travis is on Firefox 63+
+      elem.toggleAttribute = () => {};
       containerElem.appendChild(elem);
       elements[id] = elem;
     }
+    for (const className of elementClassNames) {
+      const elem = document.createElement("div");
+      elem.setAttribute("class", className);
+      containerElem.appendChild(elem);
+      elements[className] = elem;
+    }
   });
 
   afterEach(() => {
     CFRPageActions.clearRecommendations();
     containerElem.remove();
     sandbox.restore();
     globals.restore();
   });
@@ -336,17 +354,17 @@ describe("CFRPageActions", () => {
         ],
       }];
 
       beforeEach(() => {
         getStringsStub.restore();
         formatMessagesStub = sandbox.stub()
           .withArgs({id: "hello_world"})
           .resolves(localeStrings);
-        global.Localization.prototype.formatMessages = formatMessagesStub;
+        global.DOMLocalization.prototype.formatMessages = formatMessagesStub;
       });
 
       it("should return the argument if a string_id is not defined", async () => {
         assert.deepEqual(await pageAction.getStrings({}), {});
         assert.equal(await pageAction.getStrings("some string"), "some string");
       });
       it("should get the right locale string", async () => {
         assert.equal(await pageAction.getStrings({string_id: "hello_world"}), localeStrings[0].value);
@@ -393,32 +411,39 @@ describe("CFRPageActions", () => {
         await pageAction.getStrings(fromJson, "accesskey");
 
         assert.calledOnce(stub);
         stub.restore();
       });
     });
 
     describe("#_showPopupOnClick", () => {
+      let translateElementsStub;
+      let setAttributesStub;
       beforeEach(async () => {
         CFRPageActions.PageActionMap.set(fakeBrowser.ownerGlobal, pageAction);
         await CFRPageActions.addRecommendation(fakeBrowser, fakeHost, fakeRecommendation, dispatchStub);
         getStringsStub.callsFake(async a => a) // eslint-disable-line max-nested-callbacks
           .withArgs({string_id: "primary_button_id"})
           .resolves({value: "Primary Button", attributes: {accesskey: "p"}})
           .withArgs({string_id: "secondary_button_id"})
           .resolves({value: "Secondary Button", attributes: {accesskey: "s"}})
           .withArgs({string_id: "secondary_button_id_2"})
           .resolves({value: "Secondary Button 2", attributes: {accesskey: "a"}})
           .withArgs({string_id: "secondary_button_id_3"})
           .resolves({value: "Secondary Button 3", attributes: {accesskey: "g"}})
           .withArgs(sinon.match({string_id: "cfr-doorhanger-extension-learn-more-link"}))
           .resolves("Learn more")
           .withArgs(sinon.match({string_id: "cfr-doorhanger-extension-total-users"}))
           .callsFake(async ({args}) => `${args.total} users`); // eslint-disable-line max-nested-callbacks
+
+        translateElementsStub = sandbox.stub().resolves();
+        setAttributesStub = sandbox.stub();
+        global.DOMLocalization.prototype.setAttributes = setAttributesStub;
+        global.DOMLocalization.prototype.translateElements = translateElementsStub;
       });
 
       it("should call `.hideAddressBarNotifier` and do nothing if there is no recommendation for the selected browser", async () => {
         sandbox.spy(pageAction, "hideAddressBarNotifier");
         CFRPageActions.RecommendationMap.delete(fakeBrowser);
         await pageAction._showPopupOnClick({});
         assert.calledOnce(pageAction.hideAddressBarNotifier);
         assert.notCalled(global.PopupNotifications.show);
@@ -586,16 +611,46 @@ describe("CFRPageActions", () => {
           sinon.match.any, // Corresponds to the secondary action, tested above
           {
             popupIconURL: fakeRecommendation.content.addon.icon,
             hideClose: true,
             eventCallback: pageAction._popupStateChange,
           }
         );
       });
+      it("should show the bullet list details", async () => {
+        delete fakeRecommendation.content.addon;
+        await pageAction._showPopupOnClick();
+
+        assert.calledOnce(translateElementsStub);
+      });
+      it("should set the data-l10n-id on the list element", async () => {
+        delete fakeRecommendation.content.addon;
+        await pageAction._showPopupOnClick();
+
+        assert.calledOnce(setAttributesStub);
+        assert.calledWith(setAttributesStub, sinon.match.any, fakeRecommendation.content.descriptionDetails.steps[0].string_id);
+      });
+      it("should set the correct data-notification-category", async () => {
+        delete fakeRecommendation.content.addon;
+        await pageAction._showPopupOnClick();
+
+        assert.equal(elements["contextual-feature-recommendation-notification"].dataset.notificationCategory, fakeRecommendation.content.category);
+      });
+      it("should send PIN event on primary action click", async () => {
+        sandbox.stub(pageAction, "_sendTelemetry");
+        delete fakeRecommendation.content.addon;
+        await pageAction._showPopupOnClick();
+
+        const [, , , , {callback}] = global.PopupNotifications.show.firstCall.args;
+        callback();
+
+        // First call is triggered by `_showPopupOnClick`
+        assert.propertyVal(pageAction._sendTelemetry.secondCall.args[0], "event", "PIN");
+      });
     });
   });
 
   describe("CFRPageActions", () => {
     beforeEach(() => {
       // Spy on the prototype methods to inspect calls for any PageAction instance
       sandbox.spy(PageAction.prototype, "showAddressBarNotifier");
       sandbox.spy(PageAction.prototype, "hideAddressBarNotifier");
--- a/browser/components/newtab/test/unit/asrouter/constants.js
+++ b/browser/components/newtab/test/unit/asrouter/constants.js
@@ -31,13 +31,16 @@ export class FakeRemotePageManager {
         openTrustedLinkIn: sinon.stub(),
         openLinkIn: sinon.stub(),
         OpenBrowserWindow: sinon.stub(),
         openPreferences: sinon.stub(),
         gBrowser: {
           pinTab: sinon.stub(),
           selectedTab: {},
         },
+        ConfirmationHint: {
+          show: sinon.stub(),
+        },
       },
     };
     this.portID = "6000:2";
   }
 }
--- a/browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
+++ b/browser/components/newtab/test/unit/asrouter/templates/ExtensionDoorhanger.test.jsx
@@ -1,12 +1,13 @@
 import {CFRMessageProvider} from "lib/CFRMessageProvider.jsm";
 import schema from "content-src/asrouter/templates/CFR/templates/ExtensionDoorhanger.schema.json";
 
 const DEFAULT_CONTENT = {
+  "category": "dummyCategory",
   "bucket_id": "some_bucket_id",
   "notification_text": "Recommendation",
   "heading_text": "Recommended Extension",
   "info_icon": {
     "label": {"attributes": {"tooltiptext": "Why am I seeing this"}},
     "sumo_path": "extensionrecommendations",
   },
   "addon": {
@@ -34,16 +35,17 @@ const DEFAULT_CONTENT = {
         "attributes": {"accesskey": "N"},
       },
       "action": {"type": "CANCEL"},
     },
   },
 };
 
 const L10N_CONTENT = {
+  "category": "dummyL10NCategory",
   "bucket_id": "some_bucket_id",
   "notification_text": {"string_id": "notification_text_id"},
   "heading_text": {"string_id": "heading_text_id"},
   "info_icon": {
     "label": {string_id: "why_seeing_this"},
     "sumo_path": "extensionrecommendations",
   },
   "addon": {
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
@@ -0,0 +1,39 @@
+import {_DSLinkMenu as DSLinkMenu} from "content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu";
+import {LinkMenu} from "content-src/components/LinkMenu/LinkMenu";
+import React from "react";
+import {shallowWithIntl} from "test/unit/utils";
+
+describe("<DSLinkMenu>", () => {
+  const ValidDSLinkMenuProps = {
+    site: {},
+  };
+  let wrapper;
+
+  beforeEach(() => {
+    wrapper = shallowWithIntl(<DSLinkMenu {...ValidDSLinkMenuProps} />);
+  });
+
+  it("should render a context menu button", () => {
+    assert.ok(wrapper.exists());
+    assert.ok(wrapper.find(".context-menu-button").exists());
+  });
+
+  it("should render LinkMenu when context menu button is clicked", () => {
+    let button = wrapper.find(".context-menu-button");
+    button.simulate("click", {preventDefault: () => {}});
+    assert.equal(wrapper.find(LinkMenu).length, 1);
+  });
+
+  it("should pass dispatch, onUpdate, onShow, site, options, source and index to LinkMenu", () => {
+    wrapper.find(".context-menu-button").simulate("click", {preventDefault: () => {}});
+    const linkMenuProps = wrapper.find(LinkMenu).props();
+    ["dispatch", "onUpdate", "onShow", "site", "index", "options", "source"].forEach(prop => assert.property(linkMenuProps, prop));
+  });
+
+  it("should pass through the correct menu options to LinkMenu", () => {
+    wrapper.find(".context-menu-button").simulate("click", {preventDefault: () => {}});
+    const linkMenuProps = wrapper.find(LinkMenu).props();
+    assert.deepEqual(linkMenuProps.options,
+      ["OpenInNewWindow", "OpenInPrivateWindow"]);
+  });
+});
--- a/browser/components/newtab/test/unit/unit-entry.js
+++ b/browser/components/newtab/test/unit/unit-entry.js
@@ -29,16 +29,17 @@ const overrider = new GlobalOverrider();
 const TEST_GLOBAL = {
   AddonManager: {
     getActiveAddons() {
       return Promise.resolve({addons: [], fullData: false});
     },
   },
   AppConstants: {MOZILLA_OFFICIAL: true},
   UpdateUtils: {getUpdateChannel() {}},
+  BrowserWindowTracker: {getTopWindow() {}},
   ChromeUtils: {
     defineModuleGetter() {},
     generateQI() { return {}; },
     import() { return global; },
   },
   Components: {isSuccessCode: () => true},
   // eslint-disable-next-line object-shorthand
   ContentSearchUIController: function() {}, // NB: This is a function/constructor