Repository: darkreader/darkreader
Pull request #3093 (2020-07-12 21:32:16)

Partial fix for Microsoft main logo
![Microsoft Logo](https://img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE1Mu3b?ver=5c31)
[microsoft.com](https://microsoft.com)
Inverting the logo kind of works, but the background is black, not gray like the rest of the page (and it is a PNG, so I don't know how to fix background with CSS).

**Also,** when using light mode the image is now broken again, but the opposite of what it was before. I don't know how to fix this, it would be nice to just remove the background.

**Also also,** it seems like people have attempted to fix the Microsoft logo at least three times before, but they keep updating the image version. This is more of a temporary fix, I might see if I can do a more complex selector later.

GalaxySH: Funny how DR fixes the background on GitHub

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 2502f633..0f4dc2cf 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,9 +5,14 @@ module.exports = {
     rules: {
         'array-bracket-spacing': ['error', 'never'],
         'block-spacing': ['error', 'always'],
+        'comma-spacing': ['error', {
+            before: false,
+            after: true,
+        }],
         'indent': ['error', 4, {
             'SwitchCase': 1,
         }],
+        'jsx-quotes': ['error', 'prefer-double'],
         'keyword-spacing': ['error', {
             after: true,
             before: true,
diff --git a/README.md b/README.md
index 04e85869..61189f26 100644
--- a/README.md
+++ b/README.md
@@ -2,50 +2,64 @@
 
 ![Dark Reader screenshot](https://lh3.googleusercontent.com/tTJIIIAqfJWymqPM9LAmivl11kWmF-XXLABues4OwfjOED_ntsJZdLY0VTG0XFCW0W_wYSll7Q=w640-h400-e365)
 
-This extension **inverts brightness** of web pages and aims to **reduce eyestrain** while browsing the web.
+This extension **inverts brightness** of web pages and aims to **reduce eyestrain** while you browse the web.  
 Visit [Chrome Web Store](https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh)
 and [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/darkreader/)
 for more info.
 
 ## How to contribute
 
-### Sponsorship
+### Sponsor!
 [Donate](https://opencollective.com/darkreader) via Open Collective.
 
-### Translation
-Improve or suggest a translation [here](https://github.com/darkreader/darkreader/tree/master/src/_locales).
-See the list of supported [language codes](https://developer.chrome.com/webstore/i18n#localeTable).
+### Translate!
+[Improve or suggest](https://github.com/darkreader/darkreader/tree/master/src/_locales) a translation.
+See the list of [language codes](https://developer.chrome.com/webstore/i18n#localeTable) that we can support.
 
-### Fixing wrong inversion
+### Fix a wrong inversion!
 
-If some website is **already dark** (has all pages dark by default), you can **add it to [dark-sites.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dark-sites.config) file**
+If a website is **already dark** (has all pages dark by default), you can **add it to the [dark-sites.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dark-sites.config) file**
 *(please, preserve alphabetical order)*.
 
-If some **parts** of web-pages are **wrongly inverted**,
-you can specify necessary **CSS selectors** at
-**[dynamic-theme-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dynamic-theme-fixes.config) file**
+If some **parts** of a web page are **wrongly inverted**,
+you can fix this by specifying appropriate [**CSS selectors**](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors) in
+**[dynamic-theme-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dynamic-theme-fixes.config)**
 (for Dynamic Theme mode)
-or **[inversion-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/inversion-fixes.config) file**
+or **[inversion-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/inversion-fixes.config)**
 (for Filter and Filter+ modes)
-*(please, preserve alphabetical order by URL, use short selectors, preserve code style)*.
+*(please, preserve alphabetical order by URL, use short selectors, and preserve code style)*.
 
-~~Please note, that merged changes to these files are automatically delivered to all users **within 15 minutes**.~~
-Config syncing was disabled, cause the GitHub team doesn't allow using GitHub as a CDN. Storing these files and making requests to other resources will be expensive and look suspicious.
+Automatically syncing the above files to every Dark Reader user was disabled because the GitHub team doesn't allow using GitHub as a CDN. Storing these files and making requests to other resources would be expensive and look suspicious. As such, changes are included with each new Dark Reader release.
 
-### Using Dev Tools
+### Use Dev Tools!
 
-Dev Tools should be used to **fix minor issues** on a web page
-*(like dark icon on a dark background, removing bright background, adding a white background to transparent image, etc.)*.
-If the page looks partially dark and bright in **Dynamic mode**, it should be considered as a bug.
-For **Filter mode** it is a common practice to invert already dark page parts.
+Dev Tools is designed to **fix minor issues** on a web page
+*(like dark icon on dark background, removing bright background, adding white background to transparent image, etc.)*.
+If the page looks partially dark and bright in **Dynamic mode**, we consider it a bug.
+For **Filter mode**, it is a common practice to invert already dark page parts.
 
-- Open **Chrome Dev Tools** (F12).
-- Click on **element picker** (top-left corner).
+- Open **Chrome Dev Tools** (F12) in Chrome or "Inspector" (Ctrl+Shift+C) in Firefox.
+- Click on **element picker** (top-left corner). It is enabled automatically in Firefox.
 - Pick wrongly inverted element.
-- Choose a **[selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)** for that element or all similar elements (e.g. if element has `class="icon small"` selector may look like `.icon`).
+- Choose a **[selector](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors)** for that element or all similar elements (e.g. if element has `class="icon small"`, selector may look like `.icon`).
 - Click **Dark Reader icon**.
-- Click **Open developer tools** (at bottom).
-- Find or add a block containing URL and selectors to invert.
+- Click **Open developer tools** (at bottom of Dark Reader popup).
+- Edit or add a block containing the URL and selectors to invert.
+- Click **Apply**.
+- Check how it looks both in **Light** and **Dark** mode.
+- If the **fix works** open
+**[dynamic-theme-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dynamic-theme-fixes.config) file**
+or **[inversion-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/inversion-fixes.config) file**.
+- Click **Edit** (login to GitHub first).
+- **Insert your fix** there. Preserve **alphabetic order** by URL.
+- Provide a **short description** of what you have done.
+- Click **Propose file change**.
+- Review your changes. Click **Create pull request**.
+- The Travis CI will run tests reviewing your changes.
+- If you see a **red cross** click **Details** to see what is wrong and edit the existing Pull Request.
+- When you see a **green checkmark** then everything is fine.
+- Dark Reader developer will **review** and merge your changes, making them available to all users.
+
 ```
 dynamic-theme-fixes.config
 ================================
@@ -64,17 +78,48 @@ CSS
 IGNORE INLINE STYLE
 .color-picker
 
+IGNORE IMAGE ANALYSIS
+.logo
 ```
 - `INVERT` rule inverts specified elements.
-For **Dynamic mode** use `INVERT` only for dark images, that are invisible on dark backgrounds (icons, diagrams, charts, `<img>` and `<svg>` elements).
+For **Dynamic mode** use `INVERT` only for dark images that are invisible on dark backgrounds (icons, diagrams, charts, `<img>` and `<svg>` elements).
 - `CSS` rule adds custom CSS to a web page.
 `!important` keyword should be specified for each CSS property to prevent overrides by other stylesheets.
 **Dynamic mode** supports `${COLOR}` template, where `COLOR` is a color value before the inversion (`white` will become `black` in dark mode).
-- `IGNORE INLINE STYLE` rule will not analyze the inline style of matched selector's. (e.g. --darkreader-inline-bgcolor or <p style='color: red'>This will not be analyzed by dark reader if it has an matched selector</p>)
-- **Special notice for Filter and Filter+ config**.
-It works by inverting the whole web page and reverting necessary parts (images, videos, etc.), listed in the `INVERT` section.
-If inverted element contains images or other content that becomes wrongly displayed, `NO INVERT` rule can be used.
-`REMOVE BG` removes background image from element and forces black background.
+- `IGNORE INLINE STYLE` rule will prevent inline style analysis of matched elements
+(e.g. for `<p style="color: red">` element's `style` attribute will not be changed).
+- `IGNORE IMAGE ANALYSIS` rule will prevent background images from being analyzed for matched selectors.
+
+**Dynamic variables**
+
+When making a fix for background or text colors,
+instead of using hardcoded colors (like `#fff`, `#000`, `black` or `white`),
+please use CSS variables that are generated based on user settings:
+
+```
+dynamic-theme-fixes.config
+================================
+example.com
+
+CSS
+.logo {
+    background-color: var(--darkreader-neutral-background);
+}
+.footer > p {
+    color: var(--darkreader-neutral-text);
+}
+
+```
+
+Here is a full list of available CSS variables:
+
+- `--darkreader-neutral-background` should be mostly used for elements that have a wrong background color (usually bright backgrounds that should be dark).
+- `--darkreader-neutral-text` should be used for elements with a wrong text color (usually dark texts that should be light).
+- `--darkreader-selection-background` corresponds to user's Selection Background Color setting.
+- `--darkreader-selection-text` corresponds to user's Selection Text Color setting.
+
+**Fixes for Filter and Filter+ modes**.
+
 ```
 inversion-fixes.config
 ================================
@@ -97,63 +142,56 @@ CSS
     background: rgba(255, 255, 255, 0.5);
 }
 ```
-- Click **Apply**.
-- Check how it looks both in **Light** and **Dark** mode.
-- If the **fix worked** open
-**[dynamic-theme-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/dynamic-theme-fixes.config) file**
-or **[inversion-fixes.config](https://github.com/alexanderby/darkreader/blob/master/src/config/inversion-fixes.config) file**.
-- Click **edit** (sign-in to GitHub first).
-- **Insert your fix** there. Preserve **alphabetical order** by URL.
-- Provide a **short description** of what you have done.
-- Click **Propose file change**.
-- Review your changes. Click **Create pull request**.
-- GitHub will run tests reviewing your changes.
-- If you see a **red cross** click **Details** and see what is wrong and edit the existing Pull Request.
-- When you see a **green checkmark** then everything is fine.
-- Dark Reader developer will **review** and merge your changes making them available for all users.
 
-### Adding new features or fixing bugs
+- Filter and Filter+ work by inverting the whole web page and reverting necessary parts (images, videos, etc.), listed in the `INVERT` section.
+- If an inverted element contains images or other content that becomes wrongly displayed, `NO INVERT` rule can be used.
+- `REMOVE BG` removes the background image from an element and forces a black background.
 
-If you would like to **add new feature** to Dark Reader or **fix a bug**, **submit an issue** in GitHub (if there is no existing one), **discuss** it with active contributors, wait for **approval**.
+### Add new features or fix bugs!
 
-To build and debug the extension **install the [Node.js](https://nodejs.org/)** LTS.
-Install development dependencies by running `npm install` in the project root folder.
+If you would like to **add new feature** to Dark Reader or **fix a bug**, **submit an issue** in GitHub (if there is no existing one), **discuss** it with active contributors, and wait for **approval**.
+
+To build and debug the extension **install the [Node.js](https://nodejs.org/)** LTS.  
+Install development dependencies by running `npm install` in the project root folder.  
 Then execute `npm run debug`.
 
 #### Chrome
-Open the `chrome://extensions` page.
-Disable the official Dark Reader version.
-Enable the **Developer mode**.
-Click **Load unpacked extension** button, navigate to the project's `debug/` folder.
+- Open the `chrome://extensions` page.
+- Disable the official Dark Reader version.
+- Enable the **Developer mode**.
+- Click **Load unpacked extension** button
+- Navigate to the project's `debug/` folder.
 
 #### Firefox
-Open `about:addons` page.
-Disable the official Dark Reader version.
-Open `about:debugging#addons` page.
-Click **Load Temporary Add-on** button, open `debug-firefox/manifest.json` file.
+- Open the `about:addons` page.
+- Disable the official Dark Reader version.
+- Open `about:debugging#addons` page.
+- Click **Load Temporary Add-on** button
+- Open the `debug-firefox/manifest.json` file.
+
+After making any code changes, the project will be automatically recompiled.  
+If the extension **does not reload** automatically, it can be reloaded manually on the extensions page.
 
-After making any code changes the project will be automatically recompiled.
-If the extension **didn't reload** automatically it can be reloaded manually on the extensions page.
+For editing the code you can use any text editor or web IDE (like [Visual Studio Code](https://code.visualstudio.com), [Atom](https://atom.io/), or [WebStorm](https://www.jetbrains.com/webstorm/)).
 
-For editing the code you can use any text editor or web IDE (like [Visual Studio Code](https://code.visualstudio.com), [Atom](https://atom.io/), [WebStorm](https://www.jetbrains.com/webstorm/)).
-**Preserve code style** (whitespaces etc).
+**Please preserve code style** (whitespaces etc).
 
-Run tests by executing the `npm test`.
+Run tests by executing `npm test`.
 
-Submit a **pull request**, wait for **review**.
+Submit a **pull request**, and wait for **review**.
 
 ## Building for use
-You can install the extension from a file.
-Install [Node.js LTS](https://nodejs.org/en/). Download the source code (or check out from git).
-Open terminal in root folder and run:
-- `npm install`
-- `npm run release`
+You can install the extension from a file.  
+Install [Node.js LTS](https://nodejs.org/en/). Download the source code (or check out from git).  
+Open terminal in root folder and run:  
+- `npm install`  
+- `npm run release`  
 
 This will generate `build.zip` for use in Chromium browsers and `build-firefox.xpi` for use in Firefox.
 
 ## Using for a website
 
-You can use Dark Reader to enable dark mode on your website.
+You can use Dark Reader to enable dark mode on your website!
 - Install the package from NPM (`npm install darkreader`)
 - or build from the source code (`npm run api`)
 - or include the script via a CDN such as [unpkg](https://unpkg.com/darkreader/) or [jsDelivr](https://www.jsdelivr.com/package/npm/darkreader)
@@ -199,19 +237,19 @@ followSystemColorScheme();
 
 ## Contributors
 
-This project exists thanks to all the people who contribute
+This project exists thanks to all the people who contribute!
 
 <a href="../../graphs/contributors"><img src="https://opencollective.com/darkreader/contributors.svg?width=890&button=false" /></a>
 
 ## Backers
 
-Thank you to all our backers!
+Thank you to all our generous backers!
 
 <a href="https://opencollective.com/darkreader#backers" target="_blank"><img src="https://opencollective.com/darkreader/backers.svg?width=890"></a>
 
 ## Sponsors
 
-Support this project by [becoming a sponsor](https://opencollective.com/darkreader#sponsor)
+Support this project by [becoming a sponsor](https://opencollective.com/darkreader#sponsor). Thank you to our wonderful sponsors!
 
 <a href="https://opencollective.com/darkreader/sponsor/0/website" target="_blank"><img src="https://opencollective.com/darkreader/sponsor/0/avatar.svg"></a>
 <a href="https://opencollective.com/darkreader/sponsor/1/website" target="_blank"><img src="https://opencollective.com/darkreader/sponsor/1/avatar.svg"></a>
diff --git a/index.d.ts b/index.d.ts
index 6bc384e1..e7c110a4 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -73,6 +73,36 @@ declare namespace DarkReader {
          * Default 0.
          */
         textStroke: number;
+        /**
+         * Background color to use for dark mode.
+         * Default #181a1b
+         */
+        darkSchemeBackgroundColor: string;
+        /**
+         * Text color to use for dark mode.
+         * Default #e8e6e3
+         */
+        darkSchemeTextColor: string;
+        /**
+         * Background color to use for light mode.
+         * Default #dcdad7
+         */
+        lightSchemeBackgroundColor: string;
+        /**
+         * Text color to use for light mode.
+         * Default #181a1b
+         */
+        lightSchemeTextColor: string;
+        /**
+         * Scrollbar color
+         * Default auto
+         */
+        scrollbarColor: string;
+        /**
+         * Selection color
+         * Default auto
+         */
+        selectionColor: string;
     }
 
     /**
diff --git a/package-lock.json b/package-lock.json
index 963c4783..c48f9b2b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "darkreader",
-  "version": "4.9.15",
+  "version": "4.9.16",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -783,9 +783,9 @@
       }
     },
     "@rollup/plugin-commonjs": {
-      "version": "13.0.0",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-13.0.0.tgz",
-      "integrity": "sha512-Anxc3qgkAi7peAyesTqGYidG5GRim9jtg8xhmykNaZkImtvjA7Wsqep08D2mYsqw1IF7rA3lYfciLgzUSgRoqw==",
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz",
+      "integrity": "sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==",
       "dev": true,
       "requires": {
         "@rollup/pluginutils": "^3.0.8",
@@ -798,18 +798,18 @@
       }
     },
     "@rollup/plugin-node-resolve": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.1.0.tgz",
-      "integrity": "sha512-ovq7ZM3JJYUUmEjjO+H8tnUdmQmdQudJB7xruX8LFZ1W2q8jXdPUS6SsIYip8ByOApu4RR7729Am9WhCeCMiHA==",
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz",
+      "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==",
       "dev": true,
       "requires": {
-        "@rollup/pluginutils": "^3.0.8",
-        "@types/resolve": "0.0.8",
+        "@rollup/pluginutils": "^3.1.0",
+        "@types/resolve": "1.17.1",
         "builtin-modules": "^3.1.0",
         "deep-freeze": "^0.0.1",
         "deepmerge": "^4.2.2",
         "is-module": "^1.0.0",
-        "resolve": "^1.14.2"
+        "resolve": "^1.17.0"
       }
     },
     "@rollup/plugin-replace": {
@@ -901,9 +901,9 @@
       }
     },
     "@types/chrome": {
-      "version": "0.0.120",
-      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.120.tgz",
-      "integrity": "sha512-dFA5nxeccCWqPUr5y5mSKiwxIwHkySx6IAnJ2wBbnmsVvtKHuiAHpS0Cq8hyv/4aUR0KdhPD0Pp0o99E39rzFA==",
+      "version": "0.0.122",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.122.tgz",
+      "integrity": "sha512-xHmT1AlBwKAVpQmv+/5gUsB1FXLUiizIZI6bIM52DJDtEhv97FkryHkohjw2HZqAGLOxuJ3kae7YfgWIZ+hMrg==",
       "dev": true,
       "requires": {
         "@types/filesystem": "*",
@@ -984,9 +984,9 @@
       }
     },
     "@types/jest": {
-      "version": "26.0.4",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.4.tgz",
-      "integrity": "sha512-4fQNItvelbNA9+sFgU+fhJo8ZFF+AS4Egk3GWwCW2jFtViukXbnztccafAdLhzE/0EiCogljtQQXP8aQ9J7sFg==",
+      "version": "26.0.7",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.7.tgz",
+      "integrity": "sha512-+x0077/LoN6MjqBcVOe1y9dpryWnfDZ+Xfo3EqGeBcfPRJlQp3Lw62RvNlWxuGv7kOEwlHriAa54updi3Jvvwg==",
       "dev": true,
       "requires": {
         "jest-diff": "^25.2.1",
@@ -1006,9 +1006,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "14.0.22",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.22.tgz",
-      "integrity": "sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==",
+      "version": "14.0.25",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.25.tgz",
+      "integrity": "sha512-okMqUHqrMlGOxfDZliX1yFX5MV6qcd5PpRz96XYtjkM0Ws/hwg23FMUqt6pETrVRZS+EKUB5HY19mmo54EuQbA==",
       "dev": true
     },
     "@types/normalize-package-data": {
@@ -1024,9 +1024,9 @@
       "dev": true
     },
     "@types/resolve": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
-      "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==",
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
+      "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
       "dev": true,
       "requires": {
         "@types/node": "*"
@@ -1054,12 +1054,12 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz",
-      "integrity": "sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.7.0.tgz",
+      "integrity": "sha512-4OEcPON3QIx0ntsuiuFP/TkldmBGXf0uKxPQlGtS/W2F3ndYm8Vgdpj/woPJkzUc65gd3iR+qi3K8SDQP/obFg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "3.6.0",
+        "@typescript-eslint/experimental-utils": "3.7.0",
         "debug": "^4.1.1",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
@@ -1068,45 +1068,45 @@
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz",
-      "integrity": "sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.7.0.tgz",
+      "integrity": "sha512-xpfXXAfZqhhqs5RPQBfAFrWDHoNxD5+sVB5A46TF58Bq1hRfVROrWHcQHHUM9aCBdy9+cwATcvCbRg8aIRbaHQ==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/types": "3.6.0",
-        "@typescript-eslint/typescript-estree": "3.6.0",
+        "@typescript-eslint/types": "3.7.0",
+        "@typescript-eslint/typescript-estree": "3.7.0",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.0.tgz",
-      "integrity": "sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.7.0.tgz",
+      "integrity": "sha512-2LZauVUt7jAWkcIW7djUc3kyW+fSarNEuM3RF2JdLHR9BfX/nDEnyA4/uWz0wseoWVZbDXDF7iF9Jc342flNqQ==",
       "dev": true,
       "requires": {
         "@types/eslint-visitor-keys": "^1.0.0",
-        "@typescript-eslint/experimental-utils": "3.6.0",
-        "@typescript-eslint/types": "3.6.0",
-        "@typescript-eslint/typescript-estree": "3.6.0",
+        "@typescript-eslint/experimental-utils": "3.7.0",
+        "@typescript-eslint/types": "3.7.0",
+        "@typescript-eslint/typescript-estree": "3.7.0",
         "eslint-visitor-keys": "^1.1.0"
       }
     },
     "@typescript-eslint/types": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.0.tgz",
-      "integrity": "sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.7.0.tgz",
+      "integrity": "sha512-reCaK+hyKkKF+itoylAnLzFeNYAEktB0XVfSQvf0gcVgpz1l49Lt6Vo9x4MVCCxiDydA0iLAjTF/ODH0pbfnpg==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz",
-      "integrity": "sha512-G57NDSABHjvob7zVV09ehWyD1K6/YUKjz5+AufObFyjNO4DVmKejj47MHjVHHlZZKgmpJD2yyH9lfCXHrPITFg==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.7.0.tgz",
+      "integrity": "sha512-xr5oobkYRebejlACGr1TJ0Z/r0a2/HUf0SXqPvlgUMwiMqOCu/J+/Dr9U3T0IxpE5oLFSkqMx1FE/dKaZ8KsOQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "3.6.0",
-        "@typescript-eslint/visitor-keys": "3.6.0",
+        "@typescript-eslint/types": "3.7.0",
+        "@typescript-eslint/visitor-keys": "3.7.0",
         "debug": "^4.1.1",
         "glob": "^7.1.6",
         "is-glob": "^4.0.1",
@@ -1116,9 +1116,9 @@
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz",
-      "integrity": "sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==",
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.7.0.tgz",
+      "integrity": "sha512-k5PiZdB4vklUpUX4NBncn5RBKty8G3ihTY+hqJsCdMuD0v4jofI5xuqwnVcWxfv6iTm2P/dfEa2wMUnsUY8ODw==",
       "dev": true,
       "requires": {
         "eslint-visitor-keys": "^1.1.0"
@@ -1264,13 +1264,6 @@
       "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
       "dev": true
     },
-    "asap": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
-      "dev": true,
-      "optional": true
-    },
     "asn1": {
       "version": "0.2.4",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -1483,9 +1476,9 @@
       }
     },
     "binary-extensions": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
-      "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
       "dev": true
     },
     "brace-expansion": {
@@ -1610,9 +1603,9 @@
       "dev": true
     },
     "chokidar": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
-      "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz",
+      "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==",
       "dev": true,
       "requires": {
         "anymatch": "~3.1.1",
@@ -1665,12 +1658,6 @@
         "wrap-ansi": "^6.2.0"
       }
     },
-    "clone": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
-      "dev": true
-    },
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -2069,9 +2056,9 @@
       }
     },
     "eslint": {
-      "version": "7.4.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.4.0.tgz",
-      "integrity": "sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g==",
+      "version": "7.5.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.5.0.tgz",
+      "integrity": "sha512-vlUP10xse9sWt9SGRtcr1LAC67BENcQMFeV+w5EvLEoFe3xJ8cF1Skd0msziRx/VMC+72B4DxreCE+OR12OA6Q==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -2082,9 +2069,9 @@
         "doctrine": "^3.0.0",
         "enquirer": "^2.3.5",
         "eslint-scope": "^5.1.0",
-        "eslint-utils": "^2.0.0",
-        "eslint-visitor-keys": "^1.2.0",
-        "espree": "^7.1.0",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^1.3.0",
+        "espree": "^7.2.0",
         "esquery": "^1.2.0",
         "esutils": "^2.0.2",
         "file-entry-cache": "^5.0.1",
@@ -2098,7 +2085,7 @@
         "js-yaml": "^3.13.1",
         "json-stable-stringify-without-jsonify": "^1.0.1",
         "levn": "^0.4.1",
-        "lodash": "^4.17.14",
+        "lodash": "^4.17.19",
         "minimatch": "^3.0.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.1",
@@ -2144,14 +2131,14 @@
       "dev": true
     },
     "espree": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz",
-      "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz",
+      "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==",
       "dev": true,
       "requires": {
-        "acorn": "^7.2.0",
+        "acorn": "^7.3.1",
         "acorn-jsx": "^5.2.0",
-        "eslint-visitor-keys": "^1.2.0"
+        "eslint-visitor-keys": "^1.3.0"
       }
     },
     "esprima": {
@@ -3092,20 +3079,12 @@
       "dev": true
     },
     "is-reference": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.0.tgz",
-      "integrity": "sha512-ZVxq+5TkOx6GQdnoMm2aRdCKADdcrOWXLGzGT+vIA8DMpqEJaRk5AL1bS80zJ2bjHunVmjdzfCt0e4BymIEqKQ==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+      "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
       "dev": true,
       "requires": {
-        "@types/estree": "0.0.44"
-      },
-      "dependencies": {
-        "@types/estree": {
-          "version": "0.0.44",
-          "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.44.tgz",
-          "integrity": "sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g==",
-          "dev": true
-        }
+        "@types/estree": "*"
       }
     },
     "is-stream": {
@@ -4267,19 +4246,17 @@
       "dev": true
     },
     "less": {
-      "version": "3.11.3",
-      "resolved": "https://registry.npmjs.org/less/-/less-3.11.3.tgz",
-      "integrity": "sha512-VkZiTDdtNEzXA3LgjQiC3D7/ejleBPFVvq+aRI9mIj+Zhmif5TvFPM244bT4rzkvOCvJ9q4zAztok1M7Nygagw==",
+      "version": "3.12.2",
+      "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
+      "integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
       "dev": true,
       "requires": {
-        "clone": "^2.1.2",
         "errno": "^0.1.1",
         "graceful-fs": "^4.1.2",
         "image-size": "~0.5.0",
         "make-dir": "^2.1.0",
         "mime": "^1.4.1",
-        "promise": "^7.1.1",
-        "request": "^2.83.0",
+        "native-request": "^1.0.5",
         "source-map": "~0.6.0",
         "tslib": "^1.10.0"
       },
@@ -4395,9 +4372,10 @@
       }
     },
     "malevic": {
-      "version": "0.18.5",
-      "resolved": "https://registry.npmjs.org/malevic/-/malevic-0.18.5.tgz",
-      "integrity": "sha512-7fzRV8qSlYQ04hw7c77A/JTCHdqO6dbI9PcgL780C9Toad4sRr/aAEGfhjA2VmW+iOmFiKxcYBtDjx6ShbodOQ=="
+      "version": "0.18.6",
+      "resolved": "https://registry.npmjs.org/malevic/-/malevic-0.18.6.tgz",
+      "integrity": "sha512-Um4XRYJpVDhKjRRteiuHdDmcNbI5gX7URsXC6G+5Tk0Dai2W2RB39kg5C/M32IezNPudT+YsgApBh8JG6fIWrA==",
+      "dev": true
     },
     "map-cache": {
       "version": "0.2.2",
@@ -4501,10 +4479,13 @@
       }
     },
     "mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "dev": true
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5"
+      }
     },
     "ms": {
       "version": "2.1.2",
@@ -4531,6 +4512,13 @@
         "to-regex": "^3.0.1"
       }
     },
+    "native-request": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.7.tgz",
+      "integrity": "sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ==",
+      "dev": true,
+      "optional": true
+    },
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -4887,16 +4875,6 @@
       "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
       "dev": true
     },
-    "promise": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
-      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "asap": "~2.0.3"
-      }
-    },
     "prompts": {
       "version": "2.3.2",
       "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz",
@@ -5166,9 +5144,9 @@
       }
     },
     "rollup": {
-      "version": "2.21.0",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz",
-      "integrity": "sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==",
+      "version": "2.23.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.0.tgz",
+      "integrity": "sha512-vLNmZFUGVwrnqNAJ/BvuLk1MtWzu4IuoqsH9UWK5AIdO3rt8/CSiJNvPvCIvfzrbNsqKbNzPAG1V2O4eTe2XZg==",
       "dev": true,
       "requires": {
         "fsevents": "~2.1.2"
@@ -5863,9 +5841,9 @@
       "dev": true
     },
     "strip-json-comments": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
-      "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
       "dev": true
     },
     "supports-color": {
@@ -6052,21 +6030,29 @@
       }
     },
     "ts-jest": {
-      "version": "26.1.1",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.1.1.tgz",
-      "integrity": "sha512-Lk/357quLg5jJFyBQLnSbhycnB3FPe+e9i7ahxokyXxAYoB0q1pPmqxxRPYr4smJic1Rjcf7MXDBhZWgxlli0A==",
+      "version": "26.1.3",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.1.3.tgz",
+      "integrity": "sha512-beUTSvuqR9SmKQEylewqJdnXWMVGJRFqSz2M8wKJe7GBMmLZ5zw6XXKSJckbHNMxn+zdB3guN2eOucSw2gBMnw==",
       "dev": true,
       "requires": {
         "bs-logger": "0.x",
         "buffer-from": "1.x",
         "fast-json-stable-stringify": "2.x",
+        "jest-util": "26.x",
         "json5": "2.x",
         "lodash.memoize": "4.x",
         "make-error": "1.x",
-        "micromatch": "4.x",
         "mkdirp": "1.x",
         "semver": "7.x",
         "yargs-parser": "18.x"
+      },
+      "dependencies": {
+        "mkdirp": {
+          "version": "1.0.4",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+          "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+          "dev": true
+        }
       }
     },
     "ts-node": {
@@ -6172,9 +6158,9 @@
       }
     },
     "typescript": {
-      "version": "3.9.6",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz",
-      "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==",
+      "version": "3.9.7",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
+      "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
       "dev": true
     },
     "union-value": {
@@ -6420,17 +6406,6 @@
       "dev": true,
       "requires": {
         "mkdirp": "^0.5.1"
-      },
-      "dependencies": {
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        }
       }
     },
     "write-file-atomic": {
diff --git a/package.json b/package.json
index 89749e09..0f154c13 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "darkreader",
-  "version": "4.9.15",
+  "version": "4.9.16",
   "description": "Dark theme for every website",
   "scripts": {
     "api": "node tasks/build.js --api",
@@ -33,33 +33,31 @@
     "type": "opencollective",
     "url": "https://opencollective.com/darkreader"
   },
-  "dependencies": {
-    "malevic": "0.18.5"
-  },
   "devDependencies": {
-    "@rollup/plugin-commonjs": "13.0.0",
-    "@rollup/plugin-node-resolve": "8.1.0",
+    "@rollup/plugin-commonjs": "14.0.0",
+    "@rollup/plugin-node-resolve": "8.4.0",
     "@rollup/plugin-replace": "2.3.3",
     "@rollup/pluginutils": "3.1.0",
-    "@types/chrome": "0.0.120",
-    "@types/jest": "26.0.4",
-    "@types/node": "14.0.22",
-    "@typescript-eslint/eslint-plugin": "3.6.0",
-    "@typescript-eslint/parser": "3.6.0",
-    "chokidar": "3.4.0",
-    "eslint": "7.4.0",
+    "@types/chrome": "0.0.122",
+    "@types/jest": "26.0.7",
+    "@types/node": "14.0.25",
+    "@typescript-eslint/eslint-plugin": "3.7.0",
+    "@typescript-eslint/parser": "3.7.0",
+    "chokidar": "3.4.1",
+    "eslint": "7.5.0",
     "eslint-plugin-local": "1.0.0",
     "fs-extra": "9.0.1",
     "globby": "11.0.1",
     "jest": "26.1.0",
-    "less": "3.11.3",
+    "less": "3.12.2",
+    "malevic": "0.18.6",
     "prettier": "2.0.5",
-    "rollup": "2.21.0",
+    "rollup": "2.23.0",
     "rollup-plugin-typescript2": "0.27.1",
-    "ts-jest": "26.1.1",
+    "ts-jest": "26.1.3",
     "ts-node": "8.10.2",
     "tsconfig-paths": "3.9.0",
-    "typescript": "3.9.6",
+    "typescript": "3.9.7",
     "ws": "7.3.1",
     "yazl": "2.5.1"
   }
diff --git a/src/api/index.ts b/src/api/index.ts
index 966b97a0..bcb7ebcf 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -18,9 +18,8 @@ export function enable(themeOptions: Partial<Theme> = {}, fixes: DynamicThemeFix
     const theme = {...DEFAULT_THEME, ...themeOptions};
 
     if (theme.engine !== ThemeEngines.dynamicTheme) {
-        throw new Error('Theme engine is not supported');
+        throw new Error('Theme engine is not supported.');
     }
-
     createOrUpdateDynamicTheme(theme, fixes, isIFrame);
 }
 
diff --git a/src/background/config-manager.ts b/src/background/config-manager.ts
index f4c6a102..ecb4dece 100644
--- a/src/background/config-manager.ts
+++ b/src/background/config-manager.ts
@@ -77,7 +77,7 @@ export default class ConfigManager {
             local,
             localURL: CONFIG_URLs.darkSites.local,
             remoteURL: CONFIG_URLs.darkSites.remote,
-            success: ($sites) => {
+            success: ($sites: string) => {
                 this.raw.darkSites = $sites;
                 this.handleDarkSites();
             },
@@ -90,7 +90,7 @@ export default class ConfigManager {
             local,
             localURL: CONFIG_URLs.dynamicThemeFixes.local,
             remoteURL: CONFIG_URLs.dynamicThemeFixes.remote,
-            success: ($fixes) => {
+            success: ($fixes: string) => {
                 this.raw.dynamicThemeFixes = $fixes;
                 this.handleDynamicThemeFixes();
             },
@@ -103,7 +103,7 @@ export default class ConfigManager {
             local,
             localURL: CONFIG_URLs.inversionFixes.local,
             remoteURL: CONFIG_URLs.inversionFixes.remote,
-            success: ($fixes) => {
+            success: ($fixes: string) => {
                 this.raw.inversionFixes = $fixes;
                 this.handleInversionFixes();
             },
@@ -116,7 +116,7 @@ export default class ConfigManager {
             local,
             localURL: CONFIG_URLs.staticThemes.local,
             remoteURL: CONFIG_URLs.staticThemes.remote,
-            success: ($themes) => {
+            success: ($themes: string) => {
                 this.raw.staticThemes = $themes;
                 this.handleStaticThemes();
             },
diff --git a/src/background/extension.ts b/src/background/extension.ts
index 7c790a17..81b9731b 100644
--- a/src/background/extension.ts
+++ b/src/background/extension.ts
@@ -9,7 +9,7 @@ import {setWindowTheme, resetWindowTheme} from './window-theme';
 import {getFontList, getCommands, setShortcut, canInjectScript} from './utils/extension-api';
 import {isFirefox} from '../utils/platform';
 import {isInTimeInterval, getDuration, isNightAtLocation} from '../utils/time';
-import {isURLInList, getURLHost, isURLEnabled} from '../utils/url';
+import {isURLInList, getURLHostOrProtocol, isURLEnabled} from '../utils/url';
 import ThemeEngines from '../generators/theme-engines';
 import createCSSFilterStylesheet from '../generators/css-filter';
 import {getDynamicThemeFixesFor} from '../generators/dynamic-theme';
@@ -81,6 +81,9 @@ export class Extension {
         this.fonts = await getFontList();
 
         await this.user.loadSettings();
+        if (this.user.settings.syncSitesFixes) { 
+            await this.config.load({local: false});
+        }
         this.onAppToggle();
         this.changeSettings(this.user.settings);
         console.log('loaded', this.user.settings);
@@ -121,6 +124,7 @@ export class Extension {
             toggleURL: (url) => this.toggleURL(url),
             markNewsAsRead: (ids) => this.news.markAsRead(...ids),
             onPopupOpen: () => this.popupOpeningListener && this.popupOpeningListener(),
+            loadConfig: async (options) => await this.config.load(options),
             applyDevDynamicThemeFixes: (text) => this.devtools.applyDynamicThemeFixes(text),
             resetDevDynamicThemeFixes: () => this.devtools.resetDynamicThemeFixes(),
             applyDevInversionFixes: (text) => this.devtools.applyInversionFixes(text),
@@ -296,7 +300,7 @@ export class Extension {
         const siteList = isInDarkList ?
             this.user.settings.siteListEnabled.slice() :
             this.user.settings.siteList.slice();
-        const pattern = getURLHost(url);
+        const pattern = getURLHostOrProtocol(url);
         const index = siteList.indexOf(pattern);
         if (index < 0) {
             siteList.push(pattern);
@@ -372,28 +376,29 @@ export class Extension {
         const urlInfo = this.getURLInfo(url);
         if (this.isEnabled() && isURLEnabled(url, this.user.settings, urlInfo)) {
             const custom = this.user.settings.customThemes.find(({url: urlList}) => isURLInList(url, urlList));
-            const filterConfig = custom ? custom.theme : this.user.settings.theme;
+            const preset = custom ? null : this.user.settings.presets.find(({urls}) => isURLInList(url, urls));
+            const theme = custom ? custom.theme : preset ? preset.theme : this.user.settings.theme;
 
             console.log(`Creating CSS for url: ${url}`);
-            switch (filterConfig.engine) {
+            switch (theme.engine) {
                 case ThemeEngines.cssFilter: {
                     return {
                         type: 'add-css-filter',
-                        data: createCSSFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
+                        data: createCSSFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
                     };
                 }
                 case ThemeEngines.svgFilter: {
                     if (isFirefox()) {
                         return {
                             type: 'add-css-filter',
-                            data: createSVGFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
+                            data: createSVGFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
                         };
                     }
                     return {
                         type: 'add-svg-filter',
                         data: {
-                            css: createSVGFilterStylesheet(filterConfig, url, frameURL, this.config.INVERSION_FIXES),
-                            svgMatrix: getSVGFilterMatrixValue(filterConfig),
+                            css: createSVGFilterStylesheet(theme, url, frameURL, this.config.INVERSION_FIXES),
+                            svgMatrix: getSVGFilterMatrixValue(theme),
                             svgReverseMatrix: getSVGReverseFilterMatrixValue(),
                         },
                     };
@@ -401,13 +406,13 @@ export class Extension {
                 case ThemeEngines.staticTheme: {
                     return {
                         type: 'add-static-theme',
-                        data: filterConfig.stylesheet && filterConfig.stylesheet.trim() ?
-                            filterConfig.stylesheet :
-                            createStaticStylesheet(filterConfig, url, frameURL, this.config.STATIC_THEMES),
+                        data: theme.stylesheet && theme.stylesheet.trim() ?
+                            theme.stylesheet :
+                            createStaticStylesheet(theme, url, frameURL, this.config.STATIC_THEMES),
                     };
                 }
                 case ThemeEngines.dynamicTheme: {
-                    const filter = {...filterConfig};
+                    const filter = {...theme};
                     delete filter.engine;
                     const fixes = getDynamicThemeFixesFor(url, frameURL, this.config.DYNAMIC_THEME_FIXES, this.user.settings.enableForPDF);
                     const isIFrame = frameURL != null;
@@ -417,7 +422,7 @@ export class Extension {
                     };
                 }
                 default: {
-                    throw new Error(`Unknown engine ${filterConfig.engine}`);
+                    throw new Error(`Unknown engine ${theme.engine}`);
                 }
             }
         } else {
diff --git a/src/background/messenger.ts b/src/background/messenger.ts
index 510db154..edd54f7d 100644
--- a/src/background/messenger.ts
+++ b/src/background/messenger.ts
@@ -9,6 +9,7 @@ export interface ExtensionAdapter {
     markNewsAsRead: (ids: string[]) => void;
     toggleURL: (pattern: string) => void;
     onPopupOpen: () => void;
+    loadConfig: (options: {local: boolean}) => Promise<void>;
     applyDevDynamicThemeFixes: (json: string) => Error;
     resetDevDynamicThemeFixes: () => void;
     applyDevInversionFixes: (json: string) => Error;
@@ -70,7 +71,9 @@ export default class Messenger {
                 this.adapter.markNewsAsRead(data);
                 break;
             }
-
+            case 'load-config': {
+                await this.adapter.loadConfig(data);
+            }
             case 'apply-dev-dynamic-theme-fixes': {
                 const error = this.adapter.applyDevDynamicThemeFixes(data);
                 port.postMessage({id, error: (error ? error.message : null)});
diff --git a/src/background/tab-manager.ts b/src/background/tab-manager.ts
index 996885d0..036f16e0 100644
--- a/src/background/tab-manager.ts
+++ b/src/background/tab-manager.ts
@@ -79,6 +79,13 @@ export default class TabManager {
                 a.download = name;
                 a.click();
             }
+            if (type === 'request-export-css') {
+                const activeTab = await this.getActiveTab();
+                this.ports
+                    .get(activeTab.id)
+                    .get(0).port
+                    .postMessage({type: 'export-css'});
+            }
         });
     }
 
@@ -111,6 +118,9 @@ export default class TabManager {
     }
 
     async getActiveTabURL() {
+        return (await this.getActiveTab()).url;
+    }
+    async getActiveTab() {
         let tab = (await queryTabs({
             active: true,
             lastFocusedWindow: true
@@ -121,6 +131,6 @@ export default class TabManager {
             const tabs = (await queryTabs({active: true}));
             tab = tabs.find((t) => !isExtensionPage(t.url)) || tab;
         }
-        return tab.url;
+        return tab;
     }
 }
diff --git a/src/background/user-storage.ts b/src/background/user-storage.ts
index e28b5c2c..224ecda2 100644
--- a/src/background/user-storage.ts
+++ b/src/background/user-storage.ts
@@ -43,6 +43,9 @@ export default class UserStorage {
                     }
                     sync.theme = {...DEFAULT_SETTINGS.theme, ...sync.theme};
                     sync.time = {...DEFAULT_SETTINGS.time, ...sync.time};
+                    sync.presets.forEach((preset) => {
+                        preset.theme = {...DEFAULT_SETTINGS.theme, ...preset.theme};
+                    });
                     sync.customThemes.forEach((site) => {
                         site.theme = {...DEFAULT_SETTINGS.theme, ...site.theme};
                     });
diff --git a/src/config/dark-sites.config b/src/config/dark-sites.config
index 73dc8bc6..4a6d3fee 100644
--- a/src/config/dark-sites.config
+++ b/src/config/dark-sites.config
@@ -101,10 +101,12 @@ comicpunch.net
 conficturaindustries.com
 coolmathgames.com
 corepacks.com
+coriolis.io
 countermail.com
 cracked.to
 crackingking.com
 crackwatch.com
+crazygames.com
 cryptowat.ch
 cs.rin.ru
 cswarzone.com
@@ -120,6 +122,7 @@ dark.diatr.us
 darkmodelist.com
 darkreader.org
 dcards.snaz.in
+deepweblinks.net
 defanor.uberspace.net
 deltarune.com
 demonforums.net
@@ -138,12 +141,14 @@ digital.fashion
 disasm.pro
 disboard.org
 discord.bots.gg
+discord.com/app
 discord.com/channels
 discord.com/developers
 discord.com/invite
 discord.com/login
 discord.com/register
 discordbotlist.com
+discordservers.me
 discordtemplates.com
 discourse.automationgame.com
 discourse.cataclysmdda.org
@@ -170,8 +175,9 @@ ecobee.com/consumerportal
 editor.freepik.com
 editor.method.ac
 egee.io
-eggsy.codes
+eggsy.xyz
 eleaks.to
+ellie-app.com
 elybeatmaker.com
 emanuelemilella.com
 emeraldchat.com/app
@@ -198,12 +204,14 @@ find.lolpros.gg
 flixxo.com
 flooxer.com
 florr.io
+flyordie.io
 fortnite-api.com
 forum.cswarzone.com
 forum.lastos.org
 forum.privacytools.io
 forum.snahp.it
 forum.tribler.org
+forums.launchbox-app.com
 frankerfacez.com/$
 freespeechextremist.com
 funnyjunk.com
@@ -258,7 +266,6 @@ hdencode.com
 heapmedia.com
 heavybit.com
 hellofromhe.re
-henrypp.org
 hidive.com
 himovies.to
 hoarding.me
@@ -278,6 +285,7 @@ illicoweb.videotron.com
 imgbox.com
 imgops.com
 imgur.com
+indexmania.org
 indiexpo.net
 infinitezoom.net
 infocon.org
@@ -292,6 +300,7 @@ jackdaniels.com
 jakobneumann.com
 jekyllrb.com
 jellymar.io
+jhey.dev
 joeydrewstudios.com
 joinmastodon.org
 jqbx.fm
@@ -333,14 +342,17 @@ mcskinhistory.com
 mednafen.github.io
 melody.ml
 merklex.io
+metronom.us
 metropool.nl
 miaou.drycat.fr
+minehut.com
 miniroyale2.io
 mixer.com
 mixxx.org
 mkbhd.com
 mmorpg.com
 monkrus.ws
+mope.io
 mouse-sensitivity.com
 msi.com
 music.youtube.com
@@ -376,6 +388,7 @@ obsproject.com
 offshorecorptalk.com
 ogusers.com
 ohmyz.sh
+oldergeeks.com
 omgwtfnzbs.me
 onionplay.co
 open.spotify.com
@@ -413,6 +426,7 @@ playcode.io/$
 pleroma.site
 pleroma.social
 plexguide.com
+pluto.tv
 poal.co
 poe-racing.com
 poe.trade
@@ -450,8 +464,10 @@ renderlab.net
 restream4me.com
 reveddit.com
 robofight.io
+rocketleagueesports.com
 rtbyte.xyz
 runicgames.com
+rust.facepunch.com
 sa-mp.com
 sabato.studio
 sanctum.geek.nz/arabesque
@@ -482,6 +498,8 @@ skidrowreloaded.com
 skyblocknetwork.com
 slay.one
 slider.kz
+slippi.gg
+slither.io
 snazzah.com
 spacebattles.com
 spacespacespaaace.space
@@ -502,6 +520,7 @@ steamstat.us
 steamtimeidler.com
 stonestyle.co.th
 store.steampowered.com
+storyfire.com
 streamelements.com
 streamwat.ch
 studiomaertens.com
@@ -517,6 +536,7 @@ tacoanon.github.io
 takethewalk.net
 tallguysfree.com
 tautulli.com
+teambeyond.net
 teamos-hkrg.com
 teaspeak.de
 telecineplay.com.br
@@ -545,12 +565,14 @@ training.azeria-labs.com
 trellobot.xyz
 tripleaughtdesign.com
 trovo.live
+truckersmp.com
 trustreaming.tv
 ttf.tf
 tube.kdy.ch
 tuner.ninja
 twelvesmith.com
 twist.moe
+typing.works
 ufplanets.com
 unc0ver.dev
 undergroundcellar.com
@@ -585,6 +607,8 @@ wasm.continuation-labs.com/d3demo/
 weavesilk.com
 weboas.is
 wiki.step-project.com
+wolfy.fr
+wormate.io
 wowhead.com
 wowinterface.com
 www.directvnow.com
diff --git a/src/config/dynamic-theme-fixes.config b/src/config/dynamic-theme-fixes.config
index cbad0708..1985cdb7 100644
--- a/src/config/dynamic-theme-fixes.config
+++ b/src/config/dynamic-theme-fixes.config
@@ -91,6 +91,16 @@ body {
 
 ================================
 
+1917.com
+
+CSS
+body {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 2gis.*
 
 INVERT
@@ -136,6 +146,14 @@ CSS
 
 ================================
 
+abiturma.de
+
+INVERT
+div.formula
+amp-img.latex-inline
+
+================================
+
 ableton.com
 
 INVERT
@@ -153,6 +171,14 @@ div.tx.banner-workanywhere-comp.container.max-width-d
 
 ================================
 
+addons.mozilla.org
+
+IGNORE IMAGE ANALYSIS
+.Icon-youtube
+span.Permission-description:before
+
+================================
+
 adguard.com
 
 INVERT
@@ -203,6 +229,16 @@ CSS
 
 ================================
 
+alexpage.de
+
+CSS
+#page {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 aljazeera.com
 
 INVERT
@@ -221,12 +257,17 @@ CSS
 
 ================================
 
-amazon.com
+amazon.*
 amazon.co.uk
 
 INVERT
 #banner-image
 #ordersContainer .a-box.order-attributes img
+div.a-section.vse-lb-video-metadata
+div.vse-video-content
+.a-icon-close
+.a-icon-extender-expand
+.a-icon-popover
 
 CSS
 .banner-border {
@@ -240,6 +281,15 @@ img {
 
 ================================
 
+androidcentral.com
+
+CSS
+.lazy-loaded {
+    mix-blend-mode: normal !important;
+}
+
+================================
+
 angrymetalguy.com
 
 INVERT
@@ -274,10 +324,8 @@ CSS
 
 answers.unity.com
 
-CSS
-header.section-header div.shard:before {
-    background: url(//unity3d.com/profiles/unity3d/themes/unity/images/assets/layout/shard.png) 0 0 no-repeat !important;
-}
+IGNORE IMAGE ANALYSIS
+header.section-header div.shard:before
 
 ================================
 
@@ -528,6 +576,54 @@ a[href="/product"]
 
 ================================
 
+bitwit.tech
+
+CSS
+.svg-primary {
+    fill: #375D69 !important;
+}
+.svg-primary-light {
+    fill: #4A7F8F !important;
+}
+.svg-primary-dark {
+    fill: #28444D !important;
+}
+.svg-secondary {
+    fill: #B88399 !important;
+}
+.svg-secondary-light {
+    fill: #E8A9C4 !important;
+}
+.svg-secondary-dark {
+    fill: #825F6E !important;
+}
+.svg-light {
+    fill: #ADCED9 !important;
+}
+.svg-outline {
+    fill: none !important;
+    stroke: #000000 !important;
+}
+.svg-primary-outline {
+    fill: none !important;
+    stroke: #ADCED9 !important;
+}
+.svg-secondary-outline {
+    fill: none !important;
+    stroke: #375D69 !important;
+}
+.theme-light {
+    display: none !important;
+}
+.theme-dark {
+    display: none !important;
+}
+.theme-darkreader {
+    display: block !important;
+}
+
+================================
+
 blog.cloudflare.com
 
 CSS
@@ -567,6 +663,15 @@ INVERT
 
 ================================
 
+blogs.windows.com
+
+CSS
+.c-logo .c-image {
+    background: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 blueberryroasters.pl
 
 INVERT
@@ -629,6 +734,18 @@ div[role="checkbox"]
 
 ================================
 
+castbox.fm
+
+INVERT
+.nav-item.right.msg
+.nav-item.right.upload
+.nav-item.right.search
+.userNav-img[src="https://s3.castbox.fm/webstatic/images/userIcon.06c408dc.png"]
+.heart
+.playbackRate.timing.icon
+
+================================
+
 cheapshark.com
 
 CSS
@@ -638,6 +755,16 @@ CSS
 
 ================================
 
+chilkatsoft.com
+
+CSS
+body {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;   
+}
+
+================================
+
 chtoes.li
 
 INVERT
@@ -665,8 +792,8 @@ cleantechnica.com
 
 CSS
 body {
-    background-image: none !important; 
-	background-color: #1d1e1f !important; 
+    background-image: none !important;
+	background-color: #1d1e1f !important;
 }
 
 ================================
@@ -789,16 +916,8 @@ coolblue.be
 coolblue.nl
 hotorangemedia.nl
 
-CSS
-path[fill="#F60"] {
-    --darkreader-inline-fill: #F60 !important
-}
-path[fill="#FFF"] {
-    --darkreader-inline-fill: #FFF !important
-}
-path[fill="#285DAB"] {
-    --darkreader-inline-fill: #285DAB !important
-}
+IGNORE INLINE STYLE
+.header__logo-image > path
 
 ================================
 
@@ -818,6 +937,16 @@ img[title="Switch"]
 
 ================================
 
+copitosystem.com
+
+CSS
+body {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 courses.fit.cvut.cz
 
 CSS
@@ -1028,7 +1157,7 @@ nav[aria-label="Servers sidebar"] foreignObject {
     transition: all .5s;
 }
 nav[aria-label="Servers sidebar"] foreignObject:hover {
-    border-radius: 30% !important;                                                                                                                                                                                                                                       
+    border-radius: 30% !important;
 }
 
 ================================
@@ -1108,10 +1237,10 @@ downloads.khinsider.com
 
 CSS
 body {
-    background: none !important;    
+    background: none !important;
 }
 #faux {
-    background-image: none !important;    
+    background-image: none !important;
 }
 
 ================================
@@ -1121,13 +1250,21 @@ drive.google.com
 INVERT
 img[src$="google_gsuite"]
 
-CSS
-.ndfHFb-c4YZDc.ndfHFb-c4YZDc-i5oIFb .ndfHFb-c4YZDc-Bz112c:not([onclick]):not(:link):not(:visited) {
-    background-image: url('//ssl.gstatic.com/docs/common/viewer/v3/v-sprite26.svg') !important;
-}
-
 IGNORE INLINE STYLE
 div[role="presentation"] svg
+svg.a-nEbBXb-c.a-s-fa-Ha-pa
+
+IGNORE IMAGE ANALYSIS
+div.a-b.a-b-ja-el-db.a-b-Oe-n.a-b-L.a-b-Na
+
+================================
+
+droid-life.com
+
+CSS
+.collection-posts__list .preview__picture {
+    mix-blend-mode: normal !important;
+}
 
 ================================
 
@@ -1261,6 +1398,15 @@ canvas
 
 ================================
 
+expressjs.com
+
+CSS
+body {
+    background-image: none !important;
+}
+
+================================
+
 facebook.com
 
 INVERT
@@ -1354,9 +1500,6 @@ CSS
 ._4mq3 .fbNubButton {
     box-shadow: 0 0 0 1px ${rgba(0, 0, 0, .15)} !important;
 }
-#goodwill_throwback_permalink_header i {
-    background-image: url(/rsrc.php/v3/yW/r/1swgd2xJasB.png) !important;
-}
 :root, .__fb-light-mode {
     --filter-disabled-icon: invert(100%) opacity(30%) !important;
     --filter-placeholder-icon: invert(59%) sepia(11%) saturate(200%) saturate(135%) hue-rotate(176deg) brightness(96%) contrast(94%) !important;
@@ -1386,9 +1529,6 @@ div[aria-label="Change Position"] .ha302278,
 .lyi53s4r {
     background-color: rgba(88,144,255,.9) !important;
 }
-.sp_mWYnHXJ55SW {
-    background-image: url(/rsrc.php/v3/y9/r/yO1o43KF209.png) !important;
-}
 .tdjehn4e, .oo1teu6h {
     background-color: rgba(255, 255, 255, 0.1) !important;
 }
@@ -1428,43 +1568,20 @@ a[role="link"] > i.hu5pjgll, ._3w97 {
 ._2yu8 {
     background-color: rgba(255, 255, 255, .5) !important;
 }
-img[src*="map"], [style*="map"],
-._8bb_ img[src*="map"], ._8bb_ [style*="map"] {
+img[src*="/map"], [style*="map"],
+._8bb_ img[src*="/map"], ._8bb_ [style*="map"],
+img[src*="mapy.cz"], ._8bb_ img[src*="mapy.cz"],
+img[src*="%2Fmap"], ._8bb_ img[src*="%2Fmap"],
+img[src*="map.php"], ._8bb_ img[src*="map.php"],
+img[src*="www.traseo.pl%2Froute"], ._8bb_ img[src*="www.traseo.pl%2Froute"] {
     filter: invert(100%) hue-rotate(180deg) !important;
 }
-.sp_Jc8OKpJq5NW {
-    background-image: url(/rsrc.php/v3/yV/r/RO526ka7f9m.png) !important;
-}
 .q2y6ezfg, .mkbloq8g {
     background-color: var(--always-white) !important;
 }
-.sp_RsNP6McgtTN {
-    background-image: url(/rsrc.php/v3/yw/r/Jdn2b1imz7B.png) !important;
-}
-._3w97 {
-    background-image: url(/rsrc.php/v3/ya/r/4ck_WlGmssY.png) !important;
-}
 .j7vl6m33 {
     fill: var(--always-white) !important;
 }
-.sp_j_D4UkXXXbu {
-    background-image: url(/rsrc.php/v3/y-/r/kQ6of4djkqp.png) !important;
-}
-.sp_9LGGDWd8SIG {
-    background-image: url(/rsrc.php/v3/yu/r/2P7zbFKbKAC.png) !important;
-}
-.sp_1MKbKN184cB {
-    background-image: url(/rsrc.php/v3/yv/r/Sww96KKq8LS.png) !important;
-}
-.sp_ENb4WaRga4J {
-    background-image: url(/rsrc.php/v3/yy/r/8OUMcyHbiu8.png) !important;
-}
-.sp_zT9aHJDYI11 {
-    background-image: url(/rsrc.php/v3/yB/r/99Zvs7cEY2d.png) !important;
-}
-.sp_58eJ3V9_er3, .sp_tUIA9AtJipl {
-    background-image: url(/rsrc.php/v3/yd/r/jUiHkRoni5c.png) !important;
-}
 
 IGNORE INLINE STYLE
 [role="button"] svg
@@ -1474,6 +1591,9 @@ mask > rect
 mask > circle
 g > rect
 
+IGNORE IMAGE ANALYSIS
+*
+
 ================================
 
 fanatical.com
@@ -1522,13 +1642,9 @@ INVERT
 div.logo
 div.powered-by
 
-CSS
-div.logo {
-    background-image: url("https://fast.com/assets/new-logo-vert-37861c.svg") !important;
-}
-div.powered-by {
-    background-image: url("https://fast.com/assets/poweredby-865a3b.svg") !important
-}
+IGNORE IMAGE ANALYSIS
+div.logo
+div.powered-by
 
 ================================
 
@@ -1628,10 +1744,8 @@ flyzipline.com
 INVERT
 #logo
 
-CSS
-#logo {
-    background-image: url(/blogo.svg) !important;
-}
+IGNORE IMAGE ANALYSIS
+#logo
 
 ================================
 
@@ -1810,21 +1924,21 @@ CSS
     fill: ${#ebedf0} !important;
     background-color: ${#ebedf0} !important;
 }
-.day[fill="#c6e48b"], .legend li[style*="background-color: rgb(198, 228, 139)"] {
-    fill: ${#c6d4cb} !important;
-    background-color: ${#c6d4cb} !important;
+.day[fill="#9be9a8"], .legend li[style*="background-color: rgb(155, 233, 168)"] {
+    fill: ${#9be9a8} !important;
+    background-color: ${#9be9a8} !important;
 }
-.day[fill="#7bc96f"], .legend li[style*="background-color: rgb(123, 201, 111)"] {
-    fill: ${#7bb97f} !important;
-    background-color: ${#7bb97f} !important;
+.day[fill="#40c463"], .legend li[style*="background-color: rgb(64, 196, 99)"] {
+    fill: ${#40c463} !important;
+    background-color: ${#40c463} !important;
 }
-.day[fill="#239a3b"], .legend li[style*="background-color: rgb(35, 154, 59)"] {
-    fill: ${#238a3b} !important;
-    background-color: ${#238a3b} !important;
+.day[fill="#30a14e"], .legend li[style*="background-color: rgb(48, 161, 78)"] {
+    fill: ${#30a14e} !important;
+    background-color: ${#30a14e} !important;
 }
-.day[fill="#196127"], .legend li[style*="background-color: rgb(25, 97, 39)"] {
-    fill: ${#196127} !important;
-    background-color: ${#196127} !important;
+.day[fill="#216e39"], .legend li[style*="background-color: rgb(33, 110, 57)"] {
+    fill: ${#216e39} !important;
+    background-color: ${#216e39} !important;
 }
 .blob-num:not(.cc-coverage-covered-border):not(.cc-coverage-missed-border) {
     border-right: 0 !important;
@@ -1886,10 +2000,15 @@ CSS
 
 giveawayoftheday.com
 
-CSS
-.countdown-amount .diggit {
-    background: url(/images/sprite.png) 0 0 no-repeat !important;
-}
+INVERT
+.header_lang .curr_lang::before
+.header_logo
+.header_nav_trig
+.header_search
+.header_search .button
+
+IGNORE IMAGE ANALYSIS
+.countdown-amount .diggit
 
 ================================
 
@@ -1898,10 +2017,8 @@ gog.com
 INVERT
 i.icn.icn--close
 
-CSS
-.menu-anonymous__shelf {
-    background-image: url("https://menu-static.gog-statics.com/assets/img/shelf.png") !important;
-}
+IGNORE IMAGE ANALYSIS
+.menu-anonymous__shelf
 
 ================================
 
@@ -1911,9 +2028,10 @@ google.*.*/maps
 INVERT
 #app-container.vasquette:not(.app-imagery-mode):not(.app-globe-mode) .widget-scene-canvas
 #app-container.vasquette:not(.app-imagery-mode):not(.app-globe-mode) .canvas-container > canvas
+#app-container.vasquette:not(.app-imagery-mode):not(.app-globe-mode) .full-screen > img
 .widget-settings-button-icon
+.searchbox-button
 .searchbox-hamburger::before
-.searchbox-searchbutton::before
 .maps-sprite-settings-chevron-left
 a.ita-kd-icon-button > span
 li.ita-kd-menuitem > span.ita-kd-menuitem-inputtool-icon
@@ -1939,6 +2057,10 @@ a.gb_xc
 a[href*="about/products"]
 .google-logo
 .watermark
+.section-review-action-menu
+.section-review-interaction-button
+.section-directions-trip-travel-mode-icon
+.renderable-component-icon
 
 ================================
 
@@ -2270,7 +2392,6 @@ CSS
 instructure.com
 
 INVERT
-.title_3bek::after
 .equation_image
 
 ================================
@@ -2365,6 +2486,22 @@ INVERT
 
 ================================
 
+jpl.nasa.gov
+
+CSS
+.brand_area {
+   background-image: url("https://www.jpl.nasa.gov/assets/images/logo_nasa_trio_white@2x.png") !important;
+}
+
+================================
+
+jsdelivr.com
+
+INVERT
+.navbar-brand > img
+
+================================
+
 kaggle.com
 kaggleusercontent.com
 
@@ -2395,6 +2532,24 @@ INVERT
 
 ================================
 
+krita.org
+
+CSS
+.container {
+    background: none !important;
+}
+
+================================
+
+lambda-the-ultimate.org
+
+CSS
+a {
+    color: #3391ff !important;
+}
+
+================================
+
 laptopmag.com
 
 INVERT
@@ -2406,7 +2561,6 @@ div#publisherDetails.logo
 last.fm
 
 INVERT
-.header-new-love-button::before
 .resource-external-link--homepage::before
 
 CSS
@@ -2416,9 +2570,6 @@ CSS
 .user-dashboard-catalogue-item-total a {
     color: ${#666666} !important;
 }
-.masthead-logo {
-    filter: saturate(25);
-}
 .highcharts-text-outline {
     stroke: none !important;
 }
@@ -2426,6 +2577,9 @@ CSS
     color: ${#666666} !important;
 }
 
+IGNORE IMAGE ANALYSIS
+.masthead-logo
+
 ================================
 
 lastpass.com
@@ -2454,30 +2608,27 @@ CSS
 
 ================================
 
-lichess.org
+lesbonscomptes.com
 
 CSS
-.is2d .pawn.black {
-    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==') !important
-}
-.is2d .knight.black {
-    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjZWNlY2VjIiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMjQuNTUgMTAuNGwtLjQ1IDEuNDUuNS4xNWMzLjE1IDEgNS42NSAyLjQ5IDcuOSA2Ljc1UzM1Ljc1IDI5LjA2IDM1LjI1IDM5bC0uMDUuNWgyLjI1bC4wNS0uNWMuNS0xMC4wNi0uODgtMTYuODUtMy4yNS0yMS4zNC0yLjM3LTQuNDktNS43OS02LjY0LTkuMTktNy4xNmwtLjUxLS4xeiIgZmlsbD0iI2VjZWNlYyIgc3Ryb2tlPSJub25lIi8+PC9nPjwvc3ZnPg==') !important
-}
-.is2d .bishop.black {
-    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48L2c+PC9zdmc+') !important
-}
-.is2d .rook.black {
-    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy41LTdsMS41LTIuNWgxN2wxLjUgMi41aC0yMHptLS41IDR2LTRoMjF2NEgxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE0IDI5LjV2LTEzaDE3djEzSDE0eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTQgMTYuNUwxMSAxNGgyM2wtMyAyLjVIMTR6TTExIDE0VjloNHYyaDVWOWg1djJoNVY5aDR2NUgxMXoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTEyIDM1LjVoMjFtLTIwLTRoMTltLTE4LTJoMTdtLTE3LTEzaDE3TTExIDE0aDIzIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=') !important
-}
-.is2d .queen.black {
-    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIHN0cm9rZT0ibm9uZSI+PGNpcmNsZSBjeD0iNiIgY3k9IjEyIiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMTQiIGN5PSI5IiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMjIuNSIgY3k9IjgiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzMSIgY3k9IjkiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzOSIgY3k9IjEyIiByPSIyLjc1Ii8+PC9nPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi41LTEyLjVMMzEgMjVsLS4zLTE0LjEtNS4yIDEzLjYtMy0xNC41LTMgMTQuNS01LjItMTMuNkwxNCAyNSA2LjUgMTMuNSA5IDI2eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNOSAyNmMwIDIgMS41IDIgMi41IDQgMSAxLjUgMSAxIC41IDMuNS0xLjUgMS0xLjUgMi41LTEuNSAyLjUtMS41IDEuNS41IDIuNS41IDIuNSA2LjUgMSAxNi41IDEgMjMgMCAwIDAgMS41LTEgMC0yLjUgMCAwIC41LTEuNS0xLTIuNS0uNS0yLjUtLjUtMiAuNS0zLjUgMS0yIDIuNS0yIDIuNS00LTguNS0xLjUtMTguNS0xLjUtMjcgMHoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTExIDM4LjVhMzUgMzUgMSAwIDAgMjMgMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTEgMjlhMzUgMzUgMSAwIDEgMjMgMG0tMjEuNSAyLjVoMjBtLTIxIDNhMzUgMzUgMSAwIDAgMjIgMG0tMjMgM2EzNSAzNSAxIDAgMCAyNCAwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiLz48L2c+PC9zdmc+') !important
-}
-.is2d .king.black {
-    background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjYiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMjIuNSAyNXM0LjUtNy41IDMtMTAuNWMwIDAtMS0yLjUtMy0yLjVzLTMgMi41LTMgMi41Yy0xLjUgMyAzIDEwLjUgMyAxMC41IiBmaWxsPSIjMDAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjxwYXRoIGQ9Ik0xMS41IDM3YzUuNSAzLjUgMTUuNSAzLjUgMjEgMHYtN3M5LTQuNSA2LTEwLjVjLTQtNi41LTEzLjUtMy41LTE2IDRWMjd2LTMuNWMtMy41LTcuNS0xMy0xMC41LTE2LTQtMyA2IDUgMTAgNSAxMFYzN3oiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMyIDI5LjVzOC41LTQgNi4wMy05LjY1QzM0LjE1IDE0IDI1IDE4IDIyLjUgMjQuNWwuMDEgMi4xLS4wMS0yLjFDMjAgMTggOS45MDYgMTQgNi45OTcgMTkuODVjLTIuNDk3IDUuNjUgNC44NTMgOSA0Ljg1MyA5IiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMTEuNSAzMGM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwbS0yMSAzLjVjNS41LTMgMTUuNS0zIDIxIDAiIHN0cm9rZT0iI2VjZWNlYyIvPjwvZz48L3N2Zz4=') !important
+.important {
+    background-color: rgb(92, 92, 61) !important;
 }
 
 ================================
 
+lichess.org
+
+IGNORE IMAGE ANALYSIS
+.is2d .bishop.black
+.is2d .king.black
+.is2d .knight.black
+.is2d .pawn.black
+.is2d .queen.black
+.is2d .rook.black
+
+================================
+
 linkedin.com
 
 CSS
@@ -2510,6 +2661,16 @@ INVERT
 
 ================================
 
+linuxgrandma.blogspot.com
+
+CSS
+body {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 lirc.org
 
 CSS
@@ -2637,6 +2798,15 @@ IGNORE INLINE STYLE
 
 ================================
 
+manjaro.org
+
+CSS
+.page-header-image {
+    z-index: 0 !important;
+}
+
+================================
+
 mapa-turystyczna.pl
 
 INVERT
@@ -2644,6 +2814,13 @@ INVERT
 
 ================================
 
+maps.metager.de
+
+INVERT
+.map canvas
+
+================================
+
 marginalrevolution.com
 
 INVERT
@@ -2827,10 +3004,12 @@ img[src*="img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/R
 img[src*="img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE4pxBu"]
 img[src*="img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE4pkvE"]
 img[src*="/static/images/inreplyto.svg"]
+.clogo .cimage
+img[src="img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE4pndL"]
 
 CSS
 .c-logo .c-image {
-    background: ${black} !important;
+    background: transparent !important;
 }
 
 ================================
@@ -2983,10 +3162,8 @@ a.d-none.d-md-inline
 
 my.nintendo.com
 
-CSS
-.Layout-app {
-    background-image: url(https://svgshare.com/i/HcN.svg);
-}
+IGNORE IMAGE ANALYSIS
+.Layout-app
 
 ================================
 
@@ -3013,10 +3190,8 @@ INVERT
 div.logo.logo-slim
 div.logo.logo-large.logo-footer
 
-CSS
-.logo {
-    background-image: url(/pb/resources/images/wwbt-logo.svg?v=92) !important;
-}
+IGNORE IMAGE ANALYSIS
+.logo
 
 ================================
 
@@ -3065,10 +3240,6 @@ INVERT
 nextdns.io
 
 CSS
-.index .hero .content .logo {
-    background-image: url(/static/images/logo.png);
-    background: 181a1b;
-}
 .index .key-features .feature.blocklists {
     background-image: none !important;
 }
@@ -3076,6 +3247,9 @@ CSS
 IGNORE INLINE STYLE
 path.rsm-geography
 
+IGNORE IMAGE ANALYSIS
+.index .hero .content .logo
+
 ================================
 
 ngrok.com
@@ -3119,7 +3293,7 @@ notion.so
 
 CSS
 .notion-divider-block div div {
-    border-bottom: 1px solid ${rgba(55, 53, 47, 0.4)} !important;      
+    border-bottom: 1px solid ${rgba(55, 53, 47, 0.4)} !important;
 }
 
 ================================
@@ -3149,6 +3323,18 @@ CSS
 
 ================================
 
+nvidia.com
+
+INVERT
+.global-footer__logo
+
+CSS
+.brand-link.pull-left {
+    filter: invert(1) hue-rotate(170deg) brightness(4);
+}
+
+================================
+
 nvidia.com
 nvidia.in
 
@@ -3242,6 +3428,13 @@ CSS
 
 ================================
 
+olx.pl
+
+IGNORE IMAGE ANALYSIS
+.cat-icon
+
+================================
+
 omnicalculator.com
 
 INVERT
@@ -3337,7 +3530,7 @@ INVERT
 openai.com
 
 INVERT
-.release-cover-title img
+figure.release-cover>div:first-of-type>img
 
 CSS
 body {
@@ -3434,6 +3627,25 @@ CSS
 
 ================================
 
+paulgraham.com
+
+CSS
+body {
+    background-image: none !important;
+}
+
+================================
+
+peonaviveu.blogspot.com
+
+CSS
+body {
+    background-image: none !important;    
+    background-color: var(--darkreader-neutral-background) !important;                                                                                                                                                                                                                                                                     
+}
+
+================================
+
 pgatour.com
 
 CSS
@@ -3517,8 +3729,7 @@ CSS
 praca.pl
 
 INVERT
-.app-header__logo
-.szcont .f1top
+img[alt="Praca.pl"]
 
 CSS
 .listing__logo {
@@ -3528,6 +3739,13 @@ CSS
 .app-offer__content {
     background-color: rgb(25, 26, 27) !important;
 }
+.szcont .f1top {
+    background-color: rgba(0, 0, 0, 0.25) !important;
+    background-blend-mode: color;
+}
+
+IGNORE IMAGE ANALYSIS
+.szcont .f1top
 
 ================================
 
@@ -3583,7 +3801,9 @@ INVERT
 .gm-style
 
 CSS
-.cover img, .logowrapper, .restaurant-logo__inner, .orderoverview__restaurant-image-container-inner {
+.cover img, .logowrapper, .restaurant-logo__inner,
+.orderoverview__restaurant-image-container-inner,
+div[data-qa="avatar"] {
     background-color: rgba(255, 255, 255, 0.5) !important;
     background-blend-mode: color;
 }
@@ -3619,6 +3839,25 @@ td.cell, td.label {
 
 ================================
 
+rachel53461.wordpress.com
+
+CSS
+#grad {
+    background-image: none !important;
+}
+
+================================
+
+racketboy.com
+
+CSS
+#rb-split,
+#rb-split > div {
+    background-image: none !important;
+}
+
+================================
+
 railwaygazette.com
 
 INVERT
@@ -3626,12 +3865,31 @@ INVERT
 
 ================================
 
+rateyourmusic.com
+
+IGNORE IMAGE ANALYSIS
+*
+
+================================
+
 reddit.com
 
 CSS
 [style^="--pseudo-before-background"] {
     --pseudo-before-background: ${#DAE0E6} !important;
 }
+.md p>a[href="#s"]::after, a[href="#s"]::after {
+    color: #000;
+}
+
+================================
+
+redgamingtech.com
+
+CSS
+body {
+    background-image: none !important;                                                                                                                                                                                                                                                                    
+}
 
 ================================
 
@@ -3663,6 +3921,16 @@ canvas
 
 ================================
 
+relive.cc
+
+INVERT
+img[src*="logo-relive"]
+
+IGNORE IMAGE ANALYSIS
+.email-button i
+
+================================
+
 render.githubusercontent.com/view/ipynb
 
 INVERT
@@ -3740,6 +4008,24 @@ CSS
 
 ================================
 
+ros.org
+
+CSS
+.bg-ros-dots-grid {
+    background-image: none !important;
+}
+
+================================
+
+rubjo.github.io
+
+CSS
+select {
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 runkit.com
 
 INVERT
@@ -3747,6 +4033,18 @@ INVERT
 
 ================================
 
+scholar.google.*
+scholar.google.*.*
+
+INVERT
+a[role="checkbox"] > :nth-child(2)
+div[role="banner"] > a > span
+a[aria-label="Homepage"]
+.gs_ico
+img[src*="scholar_logo"]
+
+================================
+
 scp-wiki.net
 
 CSS
@@ -3928,6 +4226,15 @@ CSS
 
 ================================
 
+snack.expo.io
+
+CSS
+#root > div > div > div:nth-child(2) > div:nth-child(2) > div:nth-child(2) > div > div:nth-child(2) {
+    background-color: white !important;
+}
+
+================================
+
 softorage.com
 
 CSS
@@ -3937,6 +4244,13 @@ CSS
 
 ================================
 
+sony.*
+
+IGNORE INLINE STYLE
+.brand-logo-svg > g
+
+================================
+
 soundcloud.com
 
 INVERT
@@ -3996,6 +4310,15 @@ CSS
 
 ================================
 
+stardewvalleywiki.com
+
+CSS
+html {
+    background: none !important;
+}
+
+================================
+
 store.ubi.com
 
 INVERT
@@ -4046,6 +4369,16 @@ CSS
 
 ================================
 
+streamable.com
+
+INVERT
+.landing .features-pane .feature > span > svg
+.landing .logo
+.landing .start-pane .start-form .try-arrow > img
+#player.container .actions-section .logo
+
+================================
+
 studio.youtube.com
 
 INVERT
@@ -4151,7 +4484,7 @@ body {
 
 ================================
 
-support.discordapp.com
+support.discord.com
 
 INVERT
 .logo img
@@ -4272,6 +4605,13 @@ INVERT
 
 ================================
 
+terraform.io
+
+IGNORE INLINE STYLE
+.text
+
+================================
+
 theatlantic.com
 
 INVERT
@@ -4374,6 +4714,20 @@ h1.site-logo
 
 ================================
 
+thronemaster.net
+
+INVERT
+.head-info-wild
+
+CSS
+table.inneractionwindow,
+table.actionwindow.overlay_shadow.dlg_battle,
+table tbody tr td small i {
+    color: #000 !important;
+}
+
+================================
+
 thunderbird.net
 
 INVERT
@@ -4411,6 +4765,16 @@ div.top_section_bg, div.bottom_section_bg {
 
 ================================
 
+tonsky.me
+
+CSS
+body {
+    background-image: none !important;
+    background-color: var(--darkreader-neutral-background) !important;
+}
+
+================================
+
 translate.google.*
 translate.google.*.*
 
@@ -4433,19 +4797,11 @@ INVERT
 .ita-hwt-grip
 .gt-ex-quote
 
-CSS
-.copybutton .jfk-button-img {
-    background-image: -webkit-image-set(url(https://ssl.gstatic.com/images/icons/material/system_gm/1x/content_copy_black_24dp.png) 1x,url(https://ssl.gstatic.com/images/icons/material/system_gm/2x/content_copy_black_24dp.png) 2x) !important;
-}
-.speech-button .jfk-button-img {
-    background-image: -webkit-image-set(url(https://ssl.gstatic.com/images/icons/material/system_gm/1x/mic_black_24dp.png) 1x,url(https://ssl.gstatic.com/images/icons/material/system_gm/2x/mic_black_24dp.png) 2x) !important;
-}
-.result .unstarred > .jfk-button-img {
-    background-image: -webkit-image-set(url(https://ssl.gstatic.com/images/icons/material/system_gm/1x/star_border_black_24dp.png) 1x,url(https://ssl.gstatic.com/images/icons/material/system_gm/2x/star_border_black_24dp.png) 2x) !important;
-}
-.morebutton .jfk-button-img {
-    background-image: -webkit-image-set(url(https://ssl.gstatic.com/images/icons/material/system_gm/1x/more_vert_black_24dp.png) 1x,url(https://ssl.gstatic.com/images/icons/material/system_gm/2x/more_vert_black_24dp.png) 2x) !important;
-}
+IGNORE IMAGE ANALYSIS
+.copybutton .jfk-button-img
+.speech-button .jfk-button-img
+.result .unstarred > .jfk-button-img
+.morebutton .jfk-button-img
 
 ================================
 
@@ -4711,6 +5067,19 @@ CSS
 
 unsplash.com
 
+INVERT
+._1Jlgk
+._1Ig-9
+
+CSS
+._2AYJu._1PdBr {
+    background: ${white} !important;
+}
+
+================================
+
+unsplash.com
+
 CSS
 ._2AYJu._1PdBr {
     background: ${white} !important;
@@ -4741,6 +5110,9 @@ CSS
 .def-panel {
     border-width: medium !important;
 }
+div.slogan-container > div.word-block-wrapper {
+    color: #333 !important;
+}
 .panel {
     border-width: medium !important;
 }
@@ -5009,6 +5381,23 @@ body {
 
 ================================
 
+wiki.unity3d.com
+
+CSS
+div#content,
+#pt-login,
+div.vectorTabs,
+div.vectorTabs *,
+.portal *,
+div.vectorMenu,
+div.vectorMenu h5 a,
+div#footer,
+div#footer * {
+    background-image: none !important;
+}
+
+================================
+
 wikibooks.org
 
 INVERT
@@ -5183,6 +5572,9 @@ CSS
 html, body {
     background-image: none !important;
 }
+#buyerComments {
+    color: var(--darkreader-neutral-text) !important;
+}
 
 ================================
 
@@ -5359,6 +5751,9 @@ INVERT
 
 youtube.com
 
+INVERT
+#tube-mount .b img
+
 CSS
 html[hide-scrollbar] ::-webkit-scrollbar {
     display: none !important;
@@ -5550,8 +5945,9 @@ html:not(.style-scope) {
 .ytp-volume-slider-handle::after {
     background-color: rgba(255,255,255,.2) !important;
 }
-.ytd-toggle-button-renderer #button.style-default-active {
-    color: var(--yt-spec-call-to-action) !important;
+button[aria-label*="like this video along with"][aria-pressed="true"] > yt-icon {
+    fill: var(--yt-spec-call-to-action) !important;
+    color: var(--yt-spec-call-to-action) !important;                                                        
 }
 .ytp-menuitem-toggle-checkbox {
     background: rgba(255, 255, 255, 0.3) !important;
diff --git a/src/config/inversion-fixes.config b/src/config/inversion-fixes.config
index a17045e2..1ca70436 100644
--- a/src/config/inversion-fixes.config
+++ b/src/config/inversion-fixes.config
@@ -295,6 +295,14 @@ NO INVERT
 
 ================================
 
+dash.cloudflare.com
+
+NO INVERT
+.c_bt > div:nth-child(1) > svg:nth-child(1)
+.c_k
+
+================================
+
 dedication.pl
 
 INVERT
@@ -1119,6 +1127,15 @@ footer
 
 ================================
 
+learningsuite.byu.edu
+
+INVERT
+.sidebarCalendar
+.calendarEmpty
+.white
+
+================================
+
 libretexts.org
 
 NO INVERT
@@ -2425,6 +2442,13 @@ html.i-ua_swipe_yes .menu2__container
 
 ================================
 
+youtube-nocookie.com
+
+NO INVERT
+video
+
+================================
+
 youtube.com
 
 INVERT
diff --git a/src/defaults.ts b/src/defaults.ts
index 0bae2345..f6d182c7 100644
--- a/src/defaults.ts
+++ b/src/defaults.ts
@@ -35,6 +35,7 @@ export const DEFAULT_THEME: Theme = {
 export const DEFAULT_SETTINGS: UserSettings = {
     enabled: true,
     theme: DEFAULT_THEME,
+    presets: [],
     customThemes: [],
     siteList: [],
     siteListEnabled: [],
@@ -42,6 +43,7 @@ export const DEFAULT_SETTINGS: UserSettings = {
     changeBrowserTheme: false,
     notifyOfNews: false,
     syncSettings: true,
+    syncSitesFixes: false,
     automation: '',
     time: {
         activation: '18:00',
diff --git a/src/definitions.d.ts b/src/definitions.d.ts
index 8654f0e5..2e833b3e 100644
--- a/src/definitions.d.ts
+++ b/src/definitions.d.ts
@@ -23,6 +23,7 @@ export interface ExtensionActions {
     setShortcut(command: string, shortcut: string);
     toggleURL(url: string);
     markNewsAsRead(ids: string[]);
+    loadConfig(options: {local: boolean});
     applyDevDynamicThemeFixes(text: string): Promise<void>;
     resetDevDynamicThemeFixes();
     applyDevInversionFixes(text: string): Promise<void>;
@@ -36,7 +37,7 @@ export interface ExtWrapper {
     actions: ExtensionActions;
 }
 
-export interface FilterConfig {
+export interface Theme {
     mode: FilterMode;
     brightness: number;
     contrast: number;
@@ -55,16 +56,24 @@ export interface FilterConfig {
     selectionColor: '' | 'auto' | string;
 }
 
-export type Theme = FilterConfig;
+export type FilterConfig = Theme;
 
 export interface CustomSiteConfig {
     url: string[];
     theme: FilterConfig;
 }
 
+export interface ThemePreset {
+    id: string;
+    name: string;
+    urls: string[];
+    theme: Theme;
+}
+
 export interface UserSettings {
     enabled: boolean;
     theme: FilterConfig;
+    presets: ThemePreset[];
     customThemes: CustomSiteConfig[];
     siteList: string[];
     siteListEnabled: string[];
@@ -72,6 +81,7 @@ export interface UserSettings {
     changeBrowserTheme: boolean;
     notifyOfNews: boolean;
     syncSettings: boolean;
+    syncSitesFixes: boolean;
     automation: '' | 'time' | 'system' | 'location';
     time: TimeSettings;
     location: LocationSettings;
@@ -108,9 +118,10 @@ export interface Shortcuts {
 
 export interface DynamicThemeFix {
     url: string[];
-    ignoreInlineStyle: string[];
     invert: string[];
     css: string;
+    ignoreInlineStyle: string[];
+    ignoreImageAnalysis: string[];
 }
 
 export interface InversionFix {
diff --git a/src/generators/dynamic-theme.ts b/src/generators/dynamic-theme.ts
index ce68d56d..49a4e0a5 100644
--- a/src/generators/dynamic-theme.ts
+++ b/src/generators/dynamic-theme.ts
@@ -8,6 +8,7 @@ const dynamicThemeFixesCommands = {
     'INVERT': 'invert',
     'CSS': 'css',
     'IGNORE INLINE STYLE': 'ignoreInlineStyle',
+    'IGNORE IMAGE ANALYSIS': 'ignoreImageAnalysis',
 };
 
 export function parseDynamicThemeFixes(text: string) {
@@ -54,6 +55,7 @@ export function getDynamicThemeFixesFor(url: string, frameURL: string, fixes: Dy
         invert: fixes[0].invert || [],
         css: fixes[0].css || [],
         ignoreInlineStyle: fixes[0].ignoreInlineStyle || [],
+        ignoreImageAnalysis: fixes[0].ignoreImageAnalysis || [],
     };
     if (enabledForPDF) {
         common.invert = common.invert.concat('embed[type="application/pdf"]');
@@ -80,5 +82,6 @@ export function getDynamicThemeFixesFor(url: string, frameURL: string, fixes: Dy
         invert: common.invert.concat(match.invert || []),
         css: [common.css, match.css].filter((s) => s).join('\n'),
         ignoreInlineStyle: common.ignoreInlineStyle.concat(match.ignoreInlineStyle || []),
+        ignoreImageAnalysis: common.ignoreImageAnalysis.concat(match.ignoreImageAnalysis || []),
     };
 }
diff --git a/src/inject/dynamic-theme/adopted-style-manger.ts b/src/inject/dynamic-theme/adopted-style-manger.ts
index dde7186b..4326b6d8 100644
--- a/src/inject/dynamic-theme/adopted-style-manger.ts
+++ b/src/inject/dynamic-theme/adopted-style-manger.ts
@@ -6,7 +6,7 @@ const adoptedStyleOverrides = new WeakMap<CSSStyleSheet, CSSStyleSheet>();
 const overrideList = new WeakSet<CSSStyleSheet>();
 
 export interface AdoptedStyleSheetManager {
-    render(theme: Theme, variables: Map<string, string>): void;
+    render(theme: Theme, variables: Map<string, string>, ignoreImageAnalysis: string[]): void;
     destroy(): void;
 }
 
@@ -44,7 +44,7 @@ export function createAdoptedStyleSheetOverride(node: Document | ShadowRoot): Ad
         node.adoptedStyleSheets = newSheets;
     }
 
-    function render(theme: Theme, globalVariables: Map<string, string>) {
+    function render(theme: Theme, globalVariables: Map<string, string>, ignoreImageAnalysis: string[]) {
         node.adoptedStyleSheets.forEach((sheet) => {
             if (overrideList.has(sheet)) {
                 return;
@@ -72,6 +72,7 @@ export function createAdoptedStyleSheetOverride(node: Document | ShadowRoot): Ad
                 sourceCSSRules: rules,
                 theme,
                 variables,
+                ignoreImageAnalysis,
                 force: false,
                 isAsyncCancelled: () => cancelAsyncOperations,
             });
diff --git a/src/inject/dynamic-theme/css-collection.ts b/src/inject/dynamic-theme/css-collection.ts
new file mode 100644
index 00000000..abb97dcd
--- /dev/null
+++ b/src/inject/dynamic-theme/css-collection.ts
@@ -0,0 +1,72 @@
+import {forEach} from '../../utils/array';
+import {loadAsDataURL} from '../../utils/network';
+import {getMatches, formatCSS} from '../../utils/text';
+
+const blobRegex = /url\(\"(blob\:.*?)\"\)/g;
+
+async function replaceBlobs(text: string) {
+    const promises = [];
+    getMatches(blobRegex, text, 1).forEach((url) => {
+        const promise = loadAsDataURL(url);
+        promises.push(promise);
+    });
+    const data = await Promise.all(promises);
+    return text.replace(blobRegex, () => `url("${data.shift()}")`);
+}
+
+const banner = `/*
+                        _______
+                       /       \\
+                      .==.    .==.
+                     ((  ))==((  ))
+                    / "=="    "=="\\
+                   /____|| || ||___\\
+       ________     ____    ________  ___    ___
+       |  ___  \\   /    \\   |  ___  \\ |  |  /  /
+       |  |  \\  \\ /  /\\  \\  |  |  \\  \\|  |_/  /
+       |  |   )  /  /__\\  \\ |  |__/  /|  ___  \\
+       |  |__/  /  ______  \\|  ____  \\|  |  \\  \\
+_______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____
+|  ___  \\ |  ____/ /    \\   |  ___  \\ |  ____|  ___  \\
+|  |  \\  \\|  |___ /  /\\  \\  |  |  \\  \\|  |___|  |  \\  \\
+|  |__/  /|  ____/  /__\\  \\ |  |   )  |  ____|  |__/  /
+|  ____  \\|  |__/  ______  \\|  |__/  /|  |___|  ____  \\
+|__|   \\__\\____/__/      \\__\\_______/ |______|__|   \\__\\
+                https://darkreader.org
+*/`;
+
+export async function collectCSS() {
+    const css = [banner];
+
+    function addStaticCSS(selector: string, comment: string) {
+        const staticStyle = document.head.querySelector(selector);
+        if (staticStyle && staticStyle.textContent) {
+            css.push(`/* ${comment} */`);
+            css.push(staticStyle.textContent);
+            css.push('');
+        }
+    }
+
+    addStaticCSS('.darkreader--fallback', 'Fallback Style');
+    addStaticCSS('.darkreader--user-agent', 'User-Agent Style');
+    addStaticCSS('.darkreader--text', 'Text Style');
+    addStaticCSS('.darkreader--invert', 'Invert Style');
+    addStaticCSS('.darkreader--override', 'Override Style');
+    addStaticCSS('.darkreader--variables', 'Variables Style');
+
+    const modifiedCSS = [];
+    document.querySelectorAll('.darkreader--sync').forEach((element: HTMLStyleElement) => {
+        forEach(element.sheet.cssRules, (rule) => {
+            rule && rule.cssText && modifiedCSS.push(rule.cssText);
+        });
+    });
+
+    if (modifiedCSS.length != 0) {
+        const formattedCSS = formatCSS(modifiedCSS.join('\n'));
+        css.push('/* Modified CSS */');
+        css.push(await replaceBlobs(formattedCSS));
+        css.push('');
+    }
+
+    return css.join('\n');
+}
diff --git a/src/inject/dynamic-theme/image.ts b/src/inject/dynamic-theme/image.ts
index dbc2e6bc..604722c8 100644
--- a/src/inject/dynamic-theme/image.ts
+++ b/src/inject/dynamic-theme/image.ts
@@ -1,6 +1,6 @@
 import {getSVGFilterMatrixValue} from '../../generators/svg-filter';
 import {bgFetch} from './network';
-import {getURLHost} from '../../utils/url';
+import {getURLHostOrProtocol} from '../../utils/url';
 import {loadAsDataURL} from '../../utils/network';
 import {FilterConfig} from '../../definitions';
 
@@ -34,7 +34,7 @@ export async function getImageDetails(url: string) {
 }
 
 async function getImageDataURL(url: string) {
-    if (getURLHost(url) === location.host) {
+    if (getURLHostOrProtocol(url) === (location.host || location.protocol)) {
         return await loadAsDataURL(url);
     }
     return await bgFetch({url, responseType: 'data-url'});
diff --git a/src/inject/dynamic-theme/index.ts b/src/inject/dynamic-theme/index.ts
index c6863a97..8a03cd16 100644
--- a/src/inject/dynamic-theme/index.ts
+++ b/src/inject/dynamic-theme/index.ts
@@ -1,7 +1,7 @@
 import {replaceCSSVariables, getElementCSSVariables} from './css-rules';
 import {overrideInlineStyle, getInlineOverrideStyle, watchForInlineStyles, stopWatchingForInlineStyles, INLINE_STYLE_SELECTOR} from './inline-style';
 import {changeMetaThemeColorWhenAvailable, restoreMetaThemeColor} from './meta-theme-color';
-import {getModifiedUserAgentStyle, getModifiedFallbackStyle, cleanModificationCache, parseColorWithCache} from './modify-css';
+import {getModifiedUserAgentStyle, getModifiedFallbackStyle, cleanModificationCache, parseColorWithCache, getSelectionColor} from './modify-css';
 import {manageStyle, getManageableStyles, StyleElement, StyleManager} from './style-manager';
 import {watchForStyleChanges, stopWatchingForStyleChanges} from './watch';
 import {forEach, push, toArray} from '../../utils/array';
@@ -14,9 +14,11 @@ import {getCSSFilterValue} from '../../generators/css-filter';
 import {modifyColor} from '../../generators/modify-colors';
 import {createTextStyle} from '../../generators/text-style';
 import {FilterConfig, DynamicThemeFix} from '../../definitions';
+import {generateUID} from '../../utils/uid';
 import {createAdoptedStyleSheetOverride, AdoptedStyleSheetManager} from './adopted-style-manger';
 
 const variables = new Map<string, string>();
+const INSTANCE_ID = generateUID();
 const styleManagers = new Map<StyleElement, StyleManager>();
 const adoptedStyleManagers = [] as Array<AdoptedStyleSheetManager>;
 let filter: FilterConfig = null;
@@ -91,6 +93,20 @@ function createStaticStyleOverrides() {
     overrideStyle.textContent = fixes && fixes.css ? replaceCSSTemplates(fixes.css) : '';
     document.head.appendChild(overrideStyle);
     setupStylePositionWatcher(overrideStyle, 'override');
+
+    const variableStyle = createOrUpdateStyle('darkreader--variables');
+    const selectionColors = getSelectionColor(filter);
+    const {darkSchemeBackgroundColor, darkSchemeTextColor, lightSchemeBackgroundColor, lightSchemeTextColor} = filter;
+    variableStyle.textContent = [
+        `:root {`,
+        `   --darkreader-neutral-background: ${filter.mode === 0 ? lightSchemeBackgroundColor : darkSchemeBackgroundColor};`,
+        `   --darkreader-neutral-text: ${filter.mode === 0 ? lightSchemeTextColor : darkSchemeTextColor};`,
+        `   --darkreader-selection-background: ${selectionColors.backgroundColorSelection};`,
+        `   --darkreader-selection-text: ${selectionColors.foregroundColorSelection};`,
+        `}`
+    ].join('\n');
+    document.head.insertBefore(variableStyle, inlineStyle.nextSibling);
+    setupStylePositionWatcher(variableStyle, 'variables');
 }
 
 const shadowRootsWithOverrides = new Set<ShadowRoot>();
@@ -121,6 +137,10 @@ function cleanFallbackStyle() {
     }
 }
 
+function getIgnoreImageAnalysisSelectors() {
+    return fixes && Array.isArray(fixes.ignoreImageAnalysis) ? fixes.ignoreImageAnalysis : [];
+}
+
 function createDynamicStyleOverrides() {
     cancelRendering();
 
@@ -136,7 +156,7 @@ function createDynamicStyleOverrides() {
         .filter((details) => details && details.variables.size > 0)
         .map(({variables}) => variables);
     if (newVariables.length === 0) {
-        styleManagers.forEach((manager) => manager.render(filter, variables));
+        styleManagers.forEach((manager) => manager.render(filter, variables, getIgnoreImageAnalysisSelectors()));
         if (loadingStyles.size === 0) {
             cleanFallbackStyle();
         }
@@ -158,8 +178,8 @@ function createDynamicStyleOverrides() {
             push(inlineStyleElements, elements);
         }
     });
-    const ignoredSelectors = fixes && Array.isArray(fixes.ignoreInlineStyle) ? fixes.ignoreInlineStyle : [];
-    inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter, ignoredSelectors));
+    const ignoredInlineSelectors = fixes && Array.isArray(fixes.ignoreInlineStyle) ? fixes.ignoreInlineStyle : [];
+    inlineStyleElements.forEach((el) => overrideInlineStyle(el as HTMLElement, filter, getIgnoreImageAnalysisSelectors(), ignoredInlineSelectors));
     handleAdoptedStyleSheets(document);
 }
 
@@ -193,7 +213,7 @@ function createManager(element: StyleElement) {
             return;
         }
         if (details.variables.size === 0) {
-            manager.render(filter, variables);
+            manager.render(filter, variables, getIgnoreImageAnalysisSelectors());
         } else {
             updateVariables(details.variables);
             throttledRenderAllStyles();
@@ -227,8 +247,8 @@ function removeManager(element: StyleElement) {
 }
 
 const throttledRenderAllStyles = throttle((callback?: () => void) => {
-    styleManagers.forEach((manager) => manager.render(filter, variables));
-    adoptedStyleManagers.forEach((manager) => manager.render(filter, variables));
+    styleManagers.forEach((manager) => manager.render(filter, variables, getIgnoreImageAnalysisSelectors()));
+    adoptedStyleManagers.forEach((manager) => manager.render(filter, variables, getIgnoreImageAnalysisSelectors()));
     callback && callback();
 });
 
@@ -285,8 +305,9 @@ function handleAdoptedStyleSheets(node: ShadowRoot | Document) {
     if (Array.isArray(node.adoptedStyleSheets)) {
         if (node.adoptedStyleSheets.length > 0) {
             const newManger = createAdoptedStyleSheetOverride(node);
+
             adoptedStyleManagers.push(newManger);
-            newManger.render(filter, variables);
+            newManger.render(filter, variables, getIgnoreImageAnalysisSelectors());
         }
     }
 }
@@ -307,7 +328,7 @@ function watchForUpdates() {
             .filter((details) => details && details.variables.size > 0)
             .map(({variables}) => variables);
         if (newVariables.length === 0) {
-            newManagers.forEach((manager) => manager.render(filter, variables));
+            newManagers.forEach((manager) => manager.render(filter, variables, getIgnoreImageAnalysisSelectors()));
         } else {
             newVariables.forEach((variables) => updateVariables(variables));
             throttledRenderAllStyles();
@@ -318,9 +339,9 @@ function watchForUpdates() {
         handleAdoptedStyleSheets(shadowRoot);
     });
 
-    const ignoredSelectors = fixes && Array.isArray(fixes.ignoreInlineStyle) ? fixes.ignoreInlineStyle : [];
+    const ignoredInlineSelectors = fixes && Array.isArray(fixes.ignoreInlineStyle) ? fixes.ignoreInlineStyle : [];
     watchForInlineStyles((element) => {
-        overrideInlineStyle(element, filter, ignoredSelectors);
+        overrideInlineStyle(element, filter, ignoredInlineSelectors, getIgnoreImageAnalysisSelectors());
         if (element === document.documentElement) {
             const rootVariables = getElementCSSVariables(document.documentElement);
             if (rootVariables.size > 0) {
@@ -332,7 +353,7 @@ function watchForUpdates() {
         const inlineStyleElements = root.querySelectorAll(INLINE_STYLE_SELECTOR);
         if (inlineStyleElements.length > 0) {
             createShadowStaticStyleOverrides(root);
-            forEach(inlineStyleElements, (el) => overrideInlineStyle(el as HTMLElement, filter, ignoredSelectors));
+            forEach(inlineStyleElements, (el) => overrideInlineStyle(el as HTMLElement, filter, getIgnoreImageAnalysisSelectors(), ignoredInlineSelectors));
         }
     });
 
@@ -347,11 +368,34 @@ function stopWatchingForUpdates() {
     removeDOMReadyListener(onDOMReady);
 }
 
+function createDarkReaderInstanceMarker() {
+    const metaElement: HTMLMetaElement = document.createElement('meta');
+    metaElement.name = 'darkreader';
+    metaElement.content = INSTANCE_ID;
+    document.head.appendChild(metaElement);
+}
+
+function isAnotherDarkReaderInstanceActive() {
+    const meta: HTMLMetaElement = document.querySelector('meta[name="darkreader"]');
+    if (meta) {
+        if (meta.content !== INSTANCE_ID) {
+            return true;
+        }
+        return false;
+    } else {
+        createDarkReaderInstanceMarker();
+        return false;
+    }
+}
+
 export function createOrUpdateDynamicTheme(filterConfig: FilterConfig, dynamicThemeFixes: DynamicThemeFix, iframe: boolean) {
     filter = filterConfig;
     fixes = dynamicThemeFixes;
     isIFrame = iframe;
     if (document.head) {
+        if (isAnotherDarkReaderInstanceActive()) {
+            return;
+        }
         createThemeAndWatchForUpdates();
     } else {
         if (!isFirefox()) {
@@ -363,6 +407,10 @@ export function createOrUpdateDynamicTheme(filterConfig: FilterConfig, dynamicTh
         const headObserver = new MutationObserver(() => {
             if (document.head) {
                 headObserver.disconnect();
+                if (isAnotherDarkReaderInstanceActive()) {
+                    removeDynamicTheme();
+                    return;
+                }
                 createThemeAndWatchForUpdates();
             }
         });
@@ -380,6 +428,7 @@ export function removeDynamicTheme() {
         removeNode(document.head.querySelector('.darkreader--invert'));
         removeNode(document.head.querySelector('.darkreader--inline'));
         removeNode(document.head.querySelector('.darkreader--override'));
+        removeNode(document.head.querySelector('meta[name="darkreader"]'));
     }
     shadowRootsWithOverrides.forEach((root) => {
         removeNode(root.querySelector('.darkreader--inline'));
diff --git a/src/inject/dynamic-theme/inline-style.ts b/src/inject/dynamic-theme/inline-style.ts
index c56fd302..024371fb 100644
--- a/src/inject/dynamic-theme/inline-style.ts
+++ b/src/inject/dynamic-theme/inline-style.ts
@@ -204,7 +204,7 @@ function getInlineStyleCacheKey(el: HTMLElement, theme: FilterConfig) {
 }
 
 function shouldIgnoreInlineStyle(element: HTMLElement, selectors: string[]) {
-    for (let i = 0; i < selectors.length; i++) {
+    for (let i = 0, len = selectors.length; i < len; i++) {
         const ingnoredSelector = selectors[i];
         if (element.matches(ingnoredSelector)) {
             return true;
@@ -213,7 +213,7 @@ function shouldIgnoreInlineStyle(element: HTMLElement, selectors: string[]) {
     return false;
 }
 
-export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig, ignoreSelectors: string[]) {
+export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig, ignoreInlineSelectors: string[], ignoreImageSelectors: string[]) {
     const cacheKey = getInlineStyleCacheKey(element, theme);
     if (cacheKey === inlineStyleCache.get(element)) {
         return;
@@ -224,7 +224,7 @@ export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig, i
     function setCustomProp(targetCSSProp: string, modifierCSSProp: string, cssVal: string) {
         const {customProp, dataAttr} = overrides[targetCSSProp];
 
-        const mod = getModifiableCSSDeclaration(modifierCSSProp, cssVal, null, null);
+        const mod = getModifiableCSSDeclaration(modifierCSSProp, cssVal, null, ignoreImageSelectors, null);
         if (!mod) {
             return;
         }
@@ -239,8 +239,8 @@ export function overrideInlineStyle(element: HTMLElement, theme: FilterConfig, i
         unsetProps.delete(targetCSSProp);
     }
 
-    if (ignoreSelectors.length > 0) {
-        if (shouldIgnoreInlineStyle(element, ignoreSelectors)) {
+    if (ignoreInlineSelectors.length > 0) {
+        if (shouldIgnoreInlineStyle(element, ignoreInlineSelectors)) {
             unsetProps.forEach((cssProp) => {
                 const {store, dataAttr} = overrides[cssProp];
                 store.delete(element);
diff --git a/src/inject/dynamic-theme/modify-css.ts b/src/inject/dynamic-theme/modify-css.ts
index e03b5729..0a32a7df 100644
--- a/src/inject/dynamic-theme/modify-css.ts
+++ b/src/inject/dynamic-theme/modify-css.ts
@@ -23,7 +23,7 @@ export interface ModifiableCSSRule {
     declarations: ModifiableCSSDeclaration[];
 }
 
-export function getModifiableCSSDeclaration(property: string, value: string, rule: CSSStyleRule, isCancelled: () => boolean): ModifiableCSSDeclaration {
+export function getModifiableCSSDeclaration(property: string, value: string, rule: CSSStyleRule, ignoreImageSelectors: string[], isCancelled: () => boolean): ModifiableCSSDeclaration {
     const important = Boolean(rule && rule.style && rule.style.getPropertyPriority(property));
     const sourceValue = value;
     if (property.startsWith('--')) {
@@ -38,7 +38,7 @@ export function getModifiableCSSDeclaration(property: string, value: string, rul
             return {property, value: modifier, important, sourceValue};
         }
     } else if (property === 'background-image' || property === 'list-style-image') {
-        const modifier = getBgImageModifier(property, value, rule, isCancelled);
+        const modifier = getBgImageModifier(value, rule, ignoreImageSelectors, isCancelled);
         if (modifier) {
             return {property, value: modifier, important, sourceValue};
         }
@@ -89,8 +89,7 @@ export function getModifiedUserAgentStyle(filter: FilterConfig, isIFrame: boolea
     return lines.join('\n');
 }
 
-function getModifiedSelectionStyle(theme: Theme) {
-    const lines: string[] = [];
+export function getSelectionColor(theme: Theme) {
     let backgroundColorSelection: string;
     let foregroundColorSelection: string;
     if (theme.selectionColor === 'auto') {
@@ -106,6 +105,13 @@ function getModifiedSelectionStyle(theme: Theme) {
             foregroundColorSelection = '#000';
         }
     }
+    return {backgroundColorSelection, foregroundColorSelection};
+}
+function getModifiedSelectionStyle(theme: Theme) {
+    const lines: string[] = [];
+    const modifiedSelectionColor = getSelectionColor(theme);
+    const backgroundColorSelection = modifiedSelectionColor.backgroundColorSelection;
+    const foregroundColorSelection = modifiedSelectionColor.foregroundColorSelection;
     ['::selection', '::-moz-selection'].forEach((selection) => {
         lines.push(`${selection} {`);
         lines.push(`    background-color: ${backgroundColorSelection} !important;`);
@@ -227,7 +233,20 @@ const gradientRegex = /[\-a-z]+gradient\(([^\(\)]*(\(([^\(\)]*(\(.*?\)))*[^\(\)]
 const imageDetailsCache = new Map<string, ImageDetails>();
 const awaitingForImageLoading = new Map<string, ((imageDetails: ImageDetails) => void)[]>();
 
-function getBgImageModifier(prop: string, value: string, rule: CSSStyleRule, isCancelled: () => boolean): string | CSSValueModifier {
+function shouldIgnoreImage(element: CSSStyleRule, selectors: string[]) {
+    if (!element) {
+        return false;
+    }
+    for (let i = 0; i < selectors.length; i++) {
+        const ingnoredSelector = selectors[i];
+        if (element.selectorText.match(ingnoredSelector)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+function getBgImageModifier(value: string, rule: CSSStyleRule, ignoreImageSelectors: string[], isCancelled: () => boolean): string | CSSValueModifier {
     try {
         const gradients = getMatches(gradientRegex, value);
         const urls = getMatches(cssURLRegex, value);
@@ -305,6 +324,9 @@ function getBgImageModifier(prop: string, value: string, rule: CSSStyleRule, isC
                     imageDetails = imageDetailsCache.get(url);
                 } else {
                     try {
+                        if (shouldIgnoreImage(rule, ignoreImageSelectors)) {
+                            return null;
+                        }
                         if (awaitingForImageLoading.has(url)) {
                             const awaiters = awaitingForImageLoading.get(url);
                             imageDetails = await new Promise<ImageDetails>((resolve) => awaiters.push(resolve));
diff --git a/src/inject/dynamic-theme/style-manager.ts b/src/inject/dynamic-theme/style-manager.ts
index c81f9b51..28fdc9f0 100644
--- a/src/inject/dynamic-theme/style-manager.ts
+++ b/src/inject/dynamic-theme/style-manager.ts
@@ -31,7 +31,7 @@ export type StyleElement = HTMLLinkElement | HTMLStyleElement;
 
 export interface StyleManager {
     details(): {variables: Map<string, string>};
-    render(theme: Theme, variables: Map<string, string>): void;
+    render(theme: Theme, variables: Map<string, string>, ignoreImageAnalysis: string[]): void;
     pause(): void;
     destroy(): void;
     watch(): void;
@@ -226,7 +226,7 @@ export function manageStyle(element: StyleElement, {update, loadingStart, loadin
 
     let forceRenderStyle = false;
 
-    function render(theme: Theme, variables: Map<string, string>) {
+    function render(theme: Theme, variables: Map<string, string>, ignoreImageAnalysis: string[]) {
         const rules = getRulesSync();
         if (!rules) {
             return;
@@ -276,6 +276,7 @@ export function manageStyle(element: StyleElement, {update, loadingStart, loadin
                 sourceCSSRules: rules,
                 theme,
                 variables,
+                ignoreImageAnalysis,
                 force,
                 isAsyncCancelled: () => cancelAsyncOperations,
             });
diff --git a/src/inject/dynamic-theme/stylesheet-modifier.ts b/src/inject/dynamic-theme/stylesheet-modifier.ts
index 5c845075..d33e872b 100644
--- a/src/inject/dynamic-theme/stylesheet-modifier.ts
+++ b/src/inject/dynamic-theme/stylesheet-modifier.ts
@@ -44,6 +44,7 @@ export function createStyleSheetModifier() {
         sourceCSSRules: CSSRuleList;
         theme: Theme;
         variables: Map<string, string>;
+        ignoreImageAnalysis: string[]
         force: boolean;
         prepareSheet: () => CSSStyleSheet;
         isAsyncCancelled: () => boolean;
@@ -51,7 +52,7 @@ export function createStyleSheetModifier() {
 
     function modifySheet(options: ModifySheetOptions): void {
         const rules = options.sourceCSSRules;
-        const {theme, variables, force, prepareSheet, isAsyncCancelled} = options;
+        const {theme, variables, ignoreImageAnalysis, force, prepareSheet, isAsyncCancelled} = options;
 
         let rulesChanged = (rulesModCache.size === 0);
         const notFoundCacheKeys = new Set(rulesModCache.keys());
@@ -94,7 +95,7 @@ export function createStyleSheetModifier() {
             const modDecs: ModifiableCSSDeclaration[] = [];
             const targetRule = varsRule || rule;
             targetRule && targetRule.style && iterateCSSDeclarations(targetRule.style, (property, value) => {
-                const mod = getModifiableCSSDeclaration(property, value, rule, isAsyncCancelled);
+                const mod = getModifiableCSSDeclaration(property, value, rule, ignoreImageAnalysis, isAsyncCancelled);
                 if (mod) {
                     modDecs.push(mod);
                 }
diff --git a/src/inject/index.ts b/src/inject/index.ts
index 470c9357..c7d9bfc8 100644
--- a/src/inject/index.ts
+++ b/src/inject/index.ts
@@ -3,6 +3,7 @@ import {createOrUpdateSVGFilter, removeSVGFilter} from './svg-filter';
 import {createOrUpdateDynamicTheme, removeDynamicTheme, cleanDynamicThemeCache} from './dynamic-theme';
 import {logInfo, logWarn} from './utils/log';
 import {watchForColorSchemeChange} from './utils/watch-color-scheme';
+import {collectCSS} from './dynamic-theme/css-collection';
 
 function onMessage({type, data}) {
     switch (type) {
@@ -26,6 +27,10 @@ function onMessage({type, data}) {
             createOrUpdateDynamicTheme(filter, fixes, isIFrame);
             break;
         }
+        case 'export-css': {
+            collectCSS().then((collectedCSS) => chrome.runtime.sendMessage({type: 'export-css-response', data: collectedCSS}));
+            break;
+        }
         case 'clean-up': {
             removeStyle();
             removeSVGFilter();
diff --git a/src/inject/utils/dom.ts b/src/inject/utils/dom.ts
index f49e09db..b9546383 100644
--- a/src/inject/utils/dom.ts
+++ b/src/inject/utils/dom.ts
@@ -86,6 +86,9 @@ export function watchForNodePosition<T extends Node>(
     let start: number = null;
     let timeoutId: number = null;
     const restore = throttle(() => {
+        if (timeoutId) {
+            return;
+        }
         attempts++;
         const now = Date.now();
         if (start == null) {
@@ -96,6 +99,7 @@ export function watchForNodePosition<T extends Node>(
                 timeoutId = setTimeout(() => {
                     start = null;
                     attempts = 0;
+                    timeoutId = null;
                     restore();
                 }, RETRY_TIMEOUT);
                 return;
diff --git a/src/manifest.json b/src/manifest.json
index d15583db..70f6c82d 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -1,7 +1,7 @@
 {
     "manifest_version": 2,
     "name": "Dark Reader",
-    "version": "4.9.15",
+    "version": "4.9.16",
     "author": "Alexander Shutau",
     "description": "__MSG_extension_description__",
     "default_locale": "en",
diff --git a/src/ui/connect/connector.ts b/src/ui/connect/connector.ts
index cfd0f4c0..502c8cf6 100644
--- a/src/ui/connect/connector.ts
+++ b/src/ui/connect/connector.ts
@@ -73,6 +73,10 @@ export default class Connector implements ExtensionActions {
         this.port.postMessage({type: 'mark-news-as-read', data: ids});
     }
 
+    loadConfig(options: {local: boolean}) {
+        this.port.postMessage({type: 'load-config', data: options});
+    }
+
     applyDevDynamicThemeFixes(text: string) {
         return this.sendRequest<void>({type: 'apply-dev-dynamic-theme-fixes', data: text}, ({error}, resolve, reject) => error ? reject(error) : resolve());
     }
diff --git a/src/ui/connect/mock.ts b/src/ui/connect/mock.ts
index 7ad43aea..2a2c1427 100644
--- a/src/ui/connect/mock.ts
+++ b/src/ui/connect/mock.ts
@@ -1,4 +1,4 @@
-import {getURLHost} from '../../utils/url';
+import {getURLHostOrProtocol} from '../../utils/url';
 import {ExtensionData, TabInfo, Theme, UserSettings} from '../../definitions';
 
 export function getMockData(override = {} as Partial<ExtensionData>): ExtensionData {
@@ -7,6 +7,7 @@ export function getMockData(override = {} as Partial<ExtensionData>): ExtensionD
         isReady: true,
         settings: {
             enabled: true,
+            presets: [],
             theme: {
                 mode: 1,
                 brightness: 110,
@@ -97,7 +98,7 @@ export function createConnectorMock() {
             listener(data);
         },
         toggleURL(url) {
-            const pattern = getURLHost(url);
+            const pattern = getURLHostOrProtocol(url);
             const index = data.settings.siteList.indexOf(pattern);
             if (index >= 0) {
                 data.settings.siteList.splice(index, 1, pattern);
diff --git a/src/ui/controls/color-dropdown/index.tsx b/src/ui/controls/color-dropdown/index.tsx
index 68284384..31486565 100644
--- a/src/ui/controls/color-dropdown/index.tsx
+++ b/src/ui/controls/color-dropdown/index.tsx
@@ -28,23 +28,23 @@ export default function ColorDropDown(props: ColorDropDownProps) {
         CUSTOM: 'Custom',
     };
 
-    const dropDownValues = [
-        props.hasDefaultOption ? labels.DEFAULT : null,
-        props.hasAutoOption ? labels.AUTO : null,
-        labels.CUSTOM,
+    const dropDownOptions = [
+        props.hasDefaultOption ? {id: 'default', content: labels.DEFAULT} : null,
+        props.hasAutoOption ? {id: 'auto', content: labels.AUTO} : null,
+        {id: 'custom', content: labels.CUSTOM},
     ].filter((v) => v);
 
     const selectedDropDownValue = (
-        props.value === '' ? labels.DEFAULT :
-            props.value === 'auto' ? labels.AUTO :
-                labels.CUSTOM
+        props.value === '' ? 'default' :
+            props.value === 'auto' ? 'auto' :
+                'custom'
     );
 
     function onDropDownChange(value: string) {
         const result = {
-            [labels.DEFAULT]: '',
-            [labels.AUTO]: 'auto',
-            [labels.CUSTOM]: props.colorSuggestion,
+            default: '',
+            auto: 'auto',
+            custom: props.colorSuggestion,
         }[value];
         props.onChange(result);
     }
@@ -82,7 +82,7 @@ export default function ColorDropDown(props: ColorDropDownProps) {
             onrender={onRootRender}
         >
             <DropDown class="color-dropdown__options"
-                values={dropDownValues}
+                options={dropDownOptions}
                 selected={selectedDropDownValue}
                 onChange={onDropDownChange}
             />
diff --git a/src/ui/controls/dropdown/index.tsx b/src/ui/controls/dropdown/index.tsx
index eae08b11..c3c33d83 100644
--- a/src/ui/controls/dropdown/index.tsx
+++ b/src/ui/controls/dropdown/index.tsx
@@ -1,14 +1,16 @@
 import {m} from 'malevic';
 import {getContext} from 'malevic/dom';
 
-interface DropDownProps {
+type DropDownOption<T> = {id: T; content: Malevic.Child};
+
+interface DropDownProps<T> {
     class?: string;
-    selected: string;
-    values: string[];
-    onChange: (value: string) => void;
+    selected: T;
+    options: DropDownOption<T>[];
+    onChange: (value: T) => void;
 }
 
-export default function DropDown(props: DropDownProps) {
+export default function DropDown<T>(props: DropDownProps<T>) {
     const context = getContext();
     const store = context.store as {
         isOpen: boolean;
@@ -16,6 +18,14 @@ export default function DropDown(props: DropDownProps) {
         selectedNode: HTMLElement;
     };
 
+    if (context.prev) {
+        const currOptions = props.options.map((o) => o.id);
+        const prevOptions = (context.prev.props.options as DropDownOption<T>[]).map((o) => o.id);
+        if (currOptions.length !== prevOptions.length || currOptions.some((o, i) => o !== prevOptions[i])) {
+            store.isOpen = false;
+        }
+    }
+
     function saveListNode(el: HTMLElement) {
         store.listNode = el;
     }
@@ -49,25 +59,27 @@ export default function DropDown(props: DropDownProps) {
         }
     }
 
-    function createListItem(value: string) {
+    function createListItem(value: DropDownOption<T>) {
         return (
             <span
                 class={{
                     'dropdown__list__item': true,
-                    'dropdown__list__item--selected': value === props.selected,
+                    'dropdown__list__item--selected': value.id === props.selected,
                     [props.class]: props.class != null,
                 }}
                 onclick={() => {
                     store.isOpen = false;
                     context.refresh();
-                    props.onChange(value);
+                    props.onChange(value.id);
                 }}
             >
-                {value}
+                {value.content}
             </span>
         );
     }
 
+    const selectedContent = props.options.find((value) => value.id === props.selected).content;
+
     return (
         <span
             class={{
@@ -80,9 +92,9 @@ export default function DropDown(props: DropDownProps) {
                 class="dropdown__list"
                 oncreate={saveListNode}
             >
-                {props.values
+                {props.options
                     .slice()
-                    .sort((a, b) => a === props.selected ? -1 : b === props.selected ? 1 : 0)
+                    .sort((a, b) => a.id === props.selected ? -1 : b.id === props.selected ? 1 : 0)
                     .map(createListItem)}
             </span>
             <span
@@ -91,7 +103,7 @@ export default function DropDown(props: DropDownProps) {
                 onclick={onSelectedClick}
             >
                 <span class="dropdown__selected__text">
-                    {props.selected}
+                    {selectedContent}
                 </span>
             </span>
         </span >
diff --git a/src/ui/controls/dropdown/style.less b/src/ui/controls/dropdown/style.less
index adc509d7..afaa2a08 100644
--- a/src/ui/controls/dropdown/style.less
+++ b/src/ui/controls/dropdown/style.less
@@ -11,6 +11,7 @@
     width: 100%;
 
     &__selected {
+        align-items: center;
         background-color: @color-control-active;
         background-image: @icon-down;
         background-position: center right 0.25rem;
@@ -18,12 +19,13 @@
         background-size: 1rem;
         border-radius: @dropdown-item-height / 2;
         color: @color-control-fore;
-        display: inline-block;
+        display: inline-flex;
         height: @dropdown-item-height;
+        justify-content: center;
         line-height: @dropdown-item-height;
         margin-top: (@dropdown-height - @dropdown-item-height) / 2;
-        text-align: center;
         position: absolute;
+        text-align: center;
         width: 100%;
 
         &__text {
@@ -36,19 +38,25 @@
     }
 
     &__list {
-        background-color: @color-control-hover;
+        background-color: @color-control-back;
+        border: @size-border solid @color-border;
+        border-top: none;
         border-radius: @dropdown-item-height / 2;
         box-shadow: 0 0 0.25rem black;
+        box-sizing: border-box;
         display: none;
         margin-top: (@dropdown-height - @dropdown-item-height) / 2;
+        overflow: hidden;
         position: absolute;
         width: 100%;
 
         &__item {
+            align-items: center;
+            background-color: @color-control-back;
             color: @color-control-fore;
-            display: inline-block;
-            height: @dropdown-item-height;
-            line-height: @dropdown-item-height;
+            display: inline-flex;
+            justify-content: center;
+            min-height: @dropdown-item-height;
             text-align: center;
             width: 100%;
             
@@ -72,15 +80,13 @@
         z-index: 1;
 
         &__item {
-            height: @dropdown-item-active-height;
-            line-height: @dropdown-item-active-height;
+            min-height: @dropdown-item-active-height;
         }
     }
 
     &--open &__selected {
         border-radius: @dropdown-item-active-height / 2;
         height: @dropdown-item-active-height;
-        line-height: @dropdown-item-active-height;
         margin-top: (@dropdown-height - @dropdown-item-active-height) / 2;
         z-index: 2;
     }
diff --git a/src/ui/controls/nav-button/style.less b/src/ui/controls/nav-button/style.less
index 028b9e22..e36d014a 100644
--- a/src/ui/controls/nav-button/style.less
+++ b/src/ui/controls/nav-button/style.less
@@ -5,6 +5,7 @@
     background-position: center right 0.25rem;
     background-repeat: no-repeat;
     background-size: 1rem;
+    border-radius: @size-control-inner / 2 + @size-border;
     width: calc(100% - 2 * @size-border);
 
     &__content {
diff --git a/src/ui/controls/overlay/style.less b/src/ui/controls/overlay/style.less
index 0c1c003f..5bea3b2d 100644
--- a/src/ui/controls/overlay/style.less
+++ b/src/ui/controls/overlay/style.less
@@ -11,6 +11,7 @@
     position: fixed;
     top: 0;
     width: 100%;
+    z-index: 2;
 
     &:empty {
         display: none;
diff --git a/src/ui/controls/reset-button/style.less b/src/ui/controls/reset-button/style.less
index 05408667..6e1d3dc6 100644
--- a/src/ui/controls/reset-button/style.less
+++ b/src/ui/controls/reset-button/style.less
@@ -1,6 +1,7 @@
 @import "../../theme.less";
 
 .reset-button {
+    border-radius: @size-control-inner / 2 + @size-border;
     width: calc(100% - 2 * @size-border);
 
     &__content {
diff --git a/src/ui/controls/select/index.tsx b/src/ui/controls/select/index.tsx
index 2e6bbe07..dd6484ec 100644
--- a/src/ui/controls/select/index.tsx
+++ b/src/ui/controls/select/index.tsx
@@ -6,6 +6,7 @@ import TextBox from '../textbox';
 import VirtualScroll from '../virtual-scroll';
 
 interface SelectProps {
+    class?: any;
     value: string;
     options: {
         [value: string]: Malevic.Child;
@@ -112,7 +113,14 @@ function Select(props: SelectProps) {
     }
 
     return (
-        <span class="select" onrender={onRender}>
+        <span
+            class={[
+                'select',
+                state.isExpanded && 'select--expanded',
+                props.class,
+            ]}
+            onrender={onRender}
+        >
             <span class="select__line">
                 <TextBox
                     class="select__textbox"
diff --git a/src/ui/devtools/components/body.tsx b/src/ui/devtools/components/body.tsx
index b36e6cb8..3887c151 100644
--- a/src/ui/devtools/components/body.tsx
+++ b/src/ui/devtools/components/body.tsx
@@ -5,7 +5,7 @@ import ThemeEngines from '../../../generators/theme-engines';
 import {DEVTOOLS_DOCS_URL} from '../../../utils/links';
 import {isFirefox} from '../../../utils/platform';
 import {ExtWrapper, TabInfo} from '../../../definitions';
-import {isURLInList} from '../../../utils/url';
+import {getCurrentThemePreset} from '../../popup/theme/utils';
 
 type BodyProps = ExtWrapper & {tab: TabInfo};
 
@@ -13,8 +13,7 @@ function Body({data, tab, actions}: BodyProps) {
     const {state, setState} = useState({errorText: null as string});
     let textNode: HTMLTextAreaElement;
     const previewButtonText = data.settings.previewNewDesign ? 'Switch to old design' : 'Preview new design';
-    const custom = data.settings.customThemes.find(({url: urlList}) => isURLInList(tab.url, urlList));
-    const theme = custom ? custom.theme : data.settings.theme;
+    const {theme} = getCurrentThemePreset({data, tab, actions});
 
     const wrapper = (theme.engine === ThemeEngines.staticTheme
         ? {
diff --git a/src/ui/icons.less b/src/ui/icons.less
index 92be49f6..52ac14ed 100644
--- a/src/ui/icons.less
+++ b/src/ui/icons.less
@@ -1,3 +1,6 @@
+@icon-check: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M0,4 l2,2 l5,-5 v1.5 l-5,5 l-2,-2 z"/></svg>');
+@icon-cross-escaped-color: escape(@color-control-fore-inactive);
+@icon-cross: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="@{icon-cross-escaped-color}" d="M1,0.5 l6,6 v1.5 l-6,-6 Z M1,6.5 l6,-6 v1.5 l-6,6 z"/></svg>');
 @icon-down: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M1,2 l3,3 l3,-3 v1.5 l-3,3 l-3,-3 z"/></svg>');
 @icon-left: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M6,1 h-1.5 l-3,3 l3,3 h1.5 l-3,-3 z"/></svg>');
 @icon-minus: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M0,3 h8 v2 h-8 z"/></svg>');
diff --git a/src/ui/popup/automation-page/style.less b/src/ui/popup/automation-page/style.less
index 2e1ad141..0f0c0e00 100644
--- a/src/ui/popup/automation-page/style.less
+++ b/src/ui/popup/automation-page/style.less
@@ -1,7 +1,6 @@
 @import "../../theme";
 
 .automation-page {
-
     &__button--active {
         background: @color-control-active;
 
@@ -15,13 +14,37 @@
         flex-direction: row;
 
         .checkbox {
+            border-bottom-left-radius: @size-control-inner / 2 + @size-border;
             border-right: none;
+            border-top-left-radius: @size-control-inner / 2 + @size-border;
             flex: none;
+
+            &__checkmark {
+                background-image: @icon-cross;
+                background-position: 0.3125rem 0.25rem;
+                background-repeat: no-repeat;
+                background-size: 1rem;
+
+                &::before,
+                &::after {
+                    display: none;
+                }
+            }
+
+            &__input:checked ~ .checkbox__checkmark {
+                background-image: @icon-check;
+                background-position: 0.375rem 0.25rem;
+            }
         }
 
         .time-range-picker .time-range-picker__input--start {
             border-left: @size-border-inner solid @color-border;
         }
+
+        .time-range-picker .time-range-picker__input--end {
+            border-bottom-right-radius: @size-control-inner / 2 + @size-border;
+            border-top-right-radius: @size-control-inner / 2 + @size-border;
+        }
     }
 
     &__label {
@@ -77,7 +100,9 @@
         }
 
         &__longitude {
+            border-bottom-right-radius: @size-control-inner / 2 + @size-border;
             border-left: @size-border-inner solid @color-border;
+            border-top-right-radius: @size-control-inner / 2 + @size-border;
         }
     }
 
@@ -91,11 +116,14 @@
         }
 
         &__button {
+            border-bottom-right-radius: @size-control-inner / 2 + @size-border;
             border-left-width: @size-border-inner;
+            border-top-right-radius: @size-control-inner / 2 + @size-border;
+            color: @color-input-fore;
             flex: auto;
 
-            &:not(&--active) {
-                color: @color-input-fore;
+            &--active {
+                color: @color-control-fore;
             }
         }
 
diff --git a/src/ui/popup/body/index.tsx b/src/ui/popup/body/index.tsx
index f0324311..c12f119d 100644
--- a/src/ui/popup/body/index.tsx
+++ b/src/ui/popup/body/index.tsx
@@ -8,6 +8,7 @@ import AutomationPage from '../automation-page';
 import MainPage from '../main-page';
 import {Page, PageViewer} from '../page-viewer';
 import SettingsPage from '../settings-page';
+import SiteListPage from '../site-list-page';
 import ThemePage from '../theme/page';
 import {ViewProps} from '../types';
 import ManageSettingsPage from '../manage-settings-page';
@@ -25,10 +26,19 @@ function Logo() {
     );
 }
 
+type PageId = (
+    'main'
+    | 'theme'
+    | 'settings'
+    | 'site-list'
+    | 'automation'
+    | 'manage-settings'
+);
+
 function Pages(props: ViewProps) {
     const context = getContext();
     const store = context.store as {
-        activePage: 'main' | 'theme' | 'settings' | 'automation' | 'manage-settings';
+        activePage: PageId;
     };
     if (store.activePage == null) {
         store.activePage = 'main';
@@ -54,9 +64,15 @@ function Pages(props: ViewProps) {
         context.refresh();
     }
 
+    function onSiteListNavClick() {
+        store.activePage = 'site-list';
+        context.refresh();
+    }
+
     function onBackClick() {
         const activePage = store.activePage;
-        if (activePage === 'automation' || activePage === 'manage-settings') {
+        const settingsPageSubpages = ['automation', 'manage-settings', 'site-list'] as PageId[];
+        if (settingsPageSubpages.includes(activePage)) {
             store.activePage = 'settings';
         } else {
             store.activePage = 'main';
@@ -84,6 +100,12 @@ function Pages(props: ViewProps) {
                     {...props}
                     onAutomationNavClick={onAutomationNavClick}
                     onManageSettingsClick={onManageSettingsClick}
+                    onSiteListNavClick={onSiteListNavClick}
+                />
+            </Page>
+            <Page id="site-list">
+                <SiteListPage
+                    {...props}
                 />
             </Page>
             <Page id="automation">
@@ -112,6 +134,17 @@ function DonateGroup() {
     );
 }
 
+let appVersion: string;
+
+function AppVersion() {
+    if (!appVersion) {
+        appVersion = chrome.runtime.getManifest().version;
+    }
+    return (
+        <label class="darkreader-version">Version 5 Preview ({appVersion})</label>
+    );
+}
+
 export default function Body(props: ViewProps) {
     const context = getContext();
     context.onCreate(() => {
@@ -135,6 +168,7 @@ export default function Body(props: ViewProps) {
             <section class="m-section">
                 <DonateGroup />
             </section>
+            <AppVersion />
             <Overlay />
         </body>
     );
diff --git a/src/ui/popup/body/style.less b/src/ui/popup/body/style.less
index 293bfac9..79eeeac9 100644
--- a/src/ui/popup/body/style.less
+++ b/src/ui/popup/body/style.less
@@ -46,6 +46,7 @@
         background-repeat: no-repeat;
         background-size: 1rem;
         border: @size-border solid @color-border;
+        border-radius: @size-control-inner / 2 + @size-border;
         display: inline-flex;
         justify-content: center;
         height: @size-control-inner;
@@ -92,6 +93,7 @@
     .m-donate-button {
         align-items: center;
         background-color: @color-control-active;
+        border-radius: @size-control-inner / 2 + @size-border;
         display: inline-flex;
         justify-content: center;
         height: @size-control-inner + 2 * @size-border;
@@ -111,7 +113,8 @@
         }
     }
 
-    .m-donate-description {
+    .m-donate-description,
+    .darkreader-version {
         display: inline-block;
         font-size: @size-text-small;
         line-height: @size-text-small-height;
@@ -120,6 +123,13 @@
         user-select: none;
         width: 100%;
     }
+
+    .darkreader-version {
+        bottom: 0;
+        margin-bottom: 0.125rem;
+        position: absolute;
+        right: 0;
+    }
 }
 
 .preview .dev-tools-button {
@@ -151,8 +161,9 @@
     @import "../main-page/style.less";
     @import "../page-viewer/style.less";
     @import "../settings-page/style.less";
-    @import "../manage-settings-page/style.less";
     @import "../automation-page/style.less";
+    @import "../manage-settings-page/style.less";
+    @import "../site-list-page/style.less";
     @import "../theme/controls/theme-control.less";
     @import "../theme/controls/mode.less";
     @import "../theme/page/style.less";
diff --git a/src/ui/popup/check-button/style.less b/src/ui/popup/check-button/style.less
index b5eb34b9..83d983a8 100644
--- a/src/ui/popup/check-button/style.less
+++ b/src/ui/popup/check-button/style.less
@@ -2,10 +2,17 @@
 
 .check-button {
     .checkbox {
+        border-radius: @size-control-inner / 2 + @size-border;
         width: calc(100% - 2 * @size-border);
 
         &__checkmark {
             background-color: @color-control-active;
+            border-radius: @size-control-inner / 2;
+
+            &::before,
+            &::after {
+                display: none;
+            }
         }
 
         &__content {
@@ -21,6 +28,13 @@
         &__input:checked ~ * {
             color: @color-control-fore;
         }
+
+        &__input:checked ~ .checkbox__checkmark {
+            background-image: @icon-check;
+            background-position: center;
+            background-repeat: no-repeat;
+            background-size: 1rem;
+        }
     }
 
     &__description {
diff --git a/src/ui/popup/components/custom-settings-toggle/index.tsx b/src/ui/popup/components/custom-settings-toggle/index.tsx
index ae46d507..bd11475d 100644
--- a/src/ui/popup/components/custom-settings-toggle/index.tsx
+++ b/src/ui/popup/components/custom-settings-toggle/index.tsx
@@ -1,22 +1,20 @@
 import {m} from 'malevic';
 import {Button} from '../../../controls';
-import {getURLHost, isURLInList} from '../../../../utils/url';
+import {getURLHostOrProtocol, isURLInList} from '../../../../utils/url';
 import {getLocalMessage} from '../../../../utils/locales';
 import {ExtWrapper, TabInfo} from '../../../../definitions';
 
 export default function CustomSettingsToggle({data, tab, actions}: ExtWrapper & {tab: TabInfo}) {
-    const host = getURLHost(tab.url || '');
+    const host = getURLHostOrProtocol(tab.url);
 
     const isCustom = data.settings.customThemes.some(({url}) => isURLInList(tab.url, url));
 
-    const urlText = (host
-        ? host
-            .split('.')
-            .reduce((elements, part, i) => elements.concat(
-                <wbr />,
-                `${i > 0 ? '.' : ''}${part}`
-            ), [])
-        : 'current site');
+    const urlText = host
+        .split('.')
+        .reduce((elements, part, i) => elements.concat(
+            <wbr />,
+            `${i > 0 ? '.' : ''}${part}`
+        ), []);
 
     return (
         <Button
diff --git a/src/ui/popup/components/site-toggle/index.tsx b/src/ui/popup/components/site-toggle/index.tsx
index d5c32167..23d423dc 100644
--- a/src/ui/popup/components/site-toggle/index.tsx
+++ b/src/ui/popup/components/site-toggle/index.tsx
@@ -1,7 +1,7 @@
 import {m} from 'malevic';
 import CheckmarkIcon from './checkmark-icon';
 import {Button} from '../../../controls';
-import {getURLHost, isURLEnabled, isPDF} from '../../../../utils/url';
+import {getURLHostOrProtocol, isURLEnabled, isPDF} from '../../../../utils/url';
 import {ExtWrapper, TabInfo} from '../../../../definitions';
 
 export default function SiteToggleButton({data, tab, actions}: ExtWrapper & {tab: TabInfo}) {
@@ -19,16 +19,14 @@ export default function SiteToggleButton({data, tab, actions}: ExtWrapper & {tab
     );
     const pdf = isPDF(tab.url);
     const isSiteEnabled = isURLEnabled(tab.url, data.settings, tab);
-    const host = getURLHost(tab.url || '');
+    const host = getURLHostOrProtocol(tab.url);
 
-    const urlText = (host
-        ? host
-            .split('.')
-            .reduce((elements, part, i) => elements.concat(
-                <wbr />,
-                `${i > 0 ? '.' : ''}${part}`
-            ), [])
-        : 'current site');
+    const urlText = host
+        .split('.')
+        .reduce((elements, part, i) => elements.concat(
+            <wbr />,
+            `${i > 0 ? '.' : ''}${part}`
+        ), []);
 
     return (
         <Button
diff --git a/src/ui/popup/main-page/app-switch.tsx b/src/ui/popup/main-page/app-switch.tsx
index a89bf9e0..e3bc2bc2 100644
--- a/src/ui/popup/main-page/app-switch.tsx
+++ b/src/ui/popup/main-page/app-switch.tsx
@@ -42,7 +42,7 @@ export default function AppSwitch(props: ViewProps) {
         }
     }
 
-    const description = isOn ?
+    const descriptionText = isOn ?
         'Extension is enabled' :
         isOff ?
             'Extension is disabled' :
@@ -51,6 +51,17 @@ export default function AppSwitch(props: ViewProps) {
                 isLocationAutomation ?
                     'Switched according to location' :
                     'Switches according to system dark mode';
+    const description = (
+        <span
+            class={{
+                'app-switch__description': true,
+                'app-switch__description--on': props.data.isEnabled,
+                'app-switch__description--off': !props.data.isEnabled,
+            }}
+        >
+            {descriptionText}
+        </span>
+    );
 
     return (
         <ControlGroup class="app-switch">
diff --git a/src/ui/popup/main-page/site-toggle.tsx b/src/ui/popup/main-page/site-toggle.tsx
index f7b865bd..87a20fab 100644
--- a/src/ui/popup/main-page/site-toggle.tsx
+++ b/src/ui/popup/main-page/site-toggle.tsx
@@ -6,13 +6,23 @@ import {ViewProps} from '../types';
 
 export default function SiteToggleGroup(props: ViewProps) {
     const isPageEnabled = isURLEnabled(props.tab.url, props.data.settings, props.tab);
-    const description = isPDF(props.tab.url) ?
+    const descriptionText = isPDF(props.tab.url) ?
         isPageEnabled ?
             'Enabled for PDF files' :
             'Disabled for PDF files' :
         isPageEnabled ?
             'Enabled for current website' :
             'Disabled for current website';
+    const description = (
+        <span
+            class={{
+                'site-toggle-group__description': true,
+                'site-toggle-group__description--on': isPageEnabled,
+                'site-toggle-group__description--off': !isPageEnabled,
+            }}
+        >{descriptionText}</span>
+    );
+
     return (
         <ControlGroup class="site-toggle-group">
             <ControlGroup.Control class="site-toggle-group__control">
diff --git a/src/ui/popup/main-page/style.less b/src/ui/popup/main-page/style.less
index c51b8b04..22505218 100644
--- a/src/ui/popup/main-page/style.less
+++ b/src/ui/popup/main-page/style.less
@@ -2,7 +2,23 @@
 
 .app-switch {
     &__control {
+        border-radius: @size-control-inner / 2 + @size-border;
         width: calc(100% - @size-border * 2);
+
+        .multi-switch__highlight,
+        .multi-switch__option {
+            border-radius: @size-control-inner / 2;
+        }
+    }
+
+    &__description {
+        &--on {
+            color: @color-ok-fore;
+        }
+
+        &--off {
+            color: @color-heading;
+        }
     }
 
     &__time {
@@ -36,8 +52,19 @@
 
 .site-toggle-group {
     .site-toggle {
+        border-radius: @size-control-inner / 2 + @size-border;
         width: calc(100% - @size-border * 2);
     }
+
+    &__description {
+        &--on {
+            color: @color-ok-fore;
+        }
+
+        &--off {
+            color: @color-heading;
+        }
+    }
 }
 
 .theme-group {
@@ -48,11 +75,29 @@
     &__presets-wrapper {
         background-color: @color-control-active;
         border: @size-border solid @color-border;
+        border-radius: @size-control-inner / 2 + @size-border;
         height: @size-control-inner;
+        position: relative;
+
+        &::before {
+            border: @size-border solid @color-border;
+            border-bottom: none;
+            border-top-left-radius: @size-control-inner / 2 + @size-border;
+            border-top-right-radius: @size-control-inner / 2 + @size-border;
+            content: "";
+            display: inline-block;
+            height: @size-control-inner + 2 * @size-border;
+            left: -@size-border;
+            position: absolute;
+            top: -@size-border;
+            width: 100%;
+        }
     }
 
     &__controls-wrapper {
         border-bottom: @size-border solid @color-border;
+        border-bottom-left-radius: @size-control-inner / 2 + @size-border;
+        border-bottom-right-radius: @size-control-inner / 2 + @size-border;
         border-left: @size-border solid @color-border;
         border-right: @size-border solid @color-border;
         padding: @indent-small;
diff --git a/src/ui/popup/manage-settings-page/export-theme.tsx b/src/ui/popup/manage-settings-page/export-theme.tsx
new file mode 100644
index 00000000..48422210
--- /dev/null
+++ b/src/ui/popup/manage-settings-page/export-theme.tsx
@@ -0,0 +1,35 @@
+import {m} from 'malevic';
+import {Button} from '../../controls';
+import {saveFile} from '../../utils';
+import ControlGroup from '../control-group';
+import {getURLHostOrProtocol} from '../../../utils/url';
+
+export default function ExportTheme() {
+    const listener = ({type, data}, sender: chrome.runtime.MessageSender) => {
+        if (type === 'export-css-response') {
+            const url = getURLHostOrProtocol(sender.tab.url).replace(/[^a-z0-1\-]/g, '-');
+            saveFile(`DarkReader-${url}.css`, data);
+            chrome.runtime.onMessage.removeListener(listener);
+        }
+    };
+
+    function exportCSS() {
+        chrome.runtime.onMessage.addListener(listener);
+        chrome.runtime.sendMessage({type: 'request-export-css'});
+    }
+    return (
+        <ControlGroup>
+            <ControlGroup.Control>
+                <Button
+                    onclick={exportCSS}
+                    class="settings-button"
+                >
+                    Export Dynamic Theme
+                </Button>
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                Save generated CSS to a file
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/manage-settings-page/index.tsx b/src/ui/popup/manage-settings-page/index.tsx
index 092eb483..73030081 100644
--- a/src/ui/popup/manage-settings-page/index.tsx
+++ b/src/ui/popup/manage-settings-page/index.tsx
@@ -4,14 +4,26 @@ import ResetButtonGroup from './reset-settings-button';
 import ImportButton from './import-settings';
 import ExportButton from './export-settings';
 import SyncSettings from './sync-settings';
+import ExportTheme from './export-theme';
+import {isURLInList} from '../../../utils/url';
+import themeEngines from '../../../generators/theme-engines';
+import SyncConfigButton from './sync-config';
 
 export default function ManageSettingsPage(props: ViewProps) {
+    const custom = props.data.settings.customThemes.find(
+        ({url}) => isURLInList(props.tab.url, url)
+    );
+    const engine = custom ?
+        custom.theme.engine :
+        props.data.settings.theme.engine;
 
     return (
         <section class="m-section">
             <SyncSettings {...props} />
+            <SyncConfigButton {...props} />
             <ImportButton {...props} />
             <ExportButton {...props} />
+            {engine === themeEngines.dynamicTheme ? <ExportTheme /> : null}
             <ResetButtonGroup {...props} />
         </section>
     );
diff --git a/src/ui/popup/manage-settings-page/style.less b/src/ui/popup/manage-settings-page/style.less
index 63f49691..947f8482 100644
--- a/src/ui/popup/manage-settings-page/style.less
+++ b/src/ui/popup/manage-settings-page/style.less
@@ -1,3 +1,4 @@
 .settings-button {
+    border-radius: @size-control-inner / 2;
     width: 100%
 }
diff --git a/src/ui/popup/manage-settings-page/sync-config.tsx b/src/ui/popup/manage-settings-page/sync-config.tsx
new file mode 100644
index 00000000..7f8ccca4
--- /dev/null
+++ b/src/ui/popup/manage-settings-page/sync-config.tsx
@@ -0,0 +1,17 @@
+import {m} from 'malevic';
+import {ViewProps} from '../types';
+import CheckButton from '../check-button';
+
+export default function SyncConfigButton(props: ViewProps) {
+    function syncConfig(syncSitesFixes: boolean) {
+        props.actions.changeSettings({syncSitesFixes}); 
+        props.actions.loadConfig({local: !syncSitesFixes});
+    }
+    return (
+        <CheckButton
+            checked={props.data.settings.syncSitesFixes}
+            label='Synchronize sites fixes'
+            description='Load the latest sites fixes from a remote server'
+            onChange={syncConfig} />
+    );
+}
diff --git a/src/ui/popup/page-viewer/style.less b/src/ui/popup/page-viewer/style.less
index 499d2f96..3001911f 100644
--- a/src/ui/popup/page-viewer/style.less
+++ b/src/ui/popup/page-viewer/style.less
@@ -26,6 +26,7 @@
         background-position: center left 0.25rem;
         background-repeat: no-repeat;
         background-size: 1rem;
+        border-radius: @size-control-inner / 2 + @size-border;
         flex: none;
         margin-top: @indent-small;
         width: calc(100% - 2 * @size-border);
diff --git a/src/ui/popup/settings-page/index.tsx b/src/ui/popup/settings-page/index.tsx
index 1cf8de7d..e3946daa 100644
--- a/src/ui/popup/settings-page/index.tsx
+++ b/src/ui/popup/settings-page/index.tsx
@@ -3,6 +3,7 @@ import {ViewProps} from '../types';
 import AutomationButton from './automation-button';
 import DevToolsGroup from './devtools';
 import ManageSettingsButton from './mange-settings-button';
+import SiteListButton from './site-list-button';
 import {isFirefox} from '../../../utils/platform';
 import EnabledByDefaultGroup from './enabled-by-default';
 import InvertPDF from './invert-pdf';
@@ -10,6 +11,7 @@ import InvertPDF from './invert-pdf';
 type SettingsPageProps = ViewProps & {
     onAutomationNavClick: () => void;
     onManageSettingsClick: () => void;
+    onSiteListNavClick: () => void;
 };
 
 export default function SettingsPage(props: SettingsPageProps) {
@@ -17,6 +19,7 @@ export default function SettingsPage(props: SettingsPageProps) {
         <section class="m-section">
             <EnabledByDefaultGroup {...props} />
             {isFirefox() ? null : <InvertPDF {...props} />}
+            <SiteListButton onClick={props.onSiteListNavClick} />
             <DevToolsGroup {...props} />
             <AutomationButton onClick={props.onAutomationNavClick} />
             <ManageSettingsButton onClick={props.onManageSettingsClick} />
diff --git a/src/ui/popup/settings-page/invert-pdf.tsx b/src/ui/popup/settings-page/invert-pdf.tsx
index 66b3407f..44f2312d 100644
--- a/src/ui/popup/settings-page/invert-pdf.tsx
+++ b/src/ui/popup/settings-page/invert-pdf.tsx
@@ -10,7 +10,7 @@ export default function InvertPDF(props: ViewProps) {
     return (
         <CheckButton
             checked={props.data.settings.enableForPDF}
-            label='Enable for PDF files'
+            label="Enable for PDF files"
             description={props.data.settings.enableForPDF ?
                 'Enabled for PDF documents' :
                 'Disabled for PDF documents'}
diff --git a/src/ui/popup/settings-page/site-list-button.tsx b/src/ui/popup/settings-page/site-list-button.tsx
new file mode 100644
index 00000000..6e176047
--- /dev/null
+++ b/src/ui/popup/settings-page/site-list-button.tsx
@@ -0,0 +1,18 @@
+import {m} from 'malevic';
+import {NavButton} from '../../controls';
+import ControlGroup from '../control-group';
+
+export default function SiteListButton(props: {onClick: () => void}) {
+    return (
+        <ControlGroup>
+            <ControlGroup.Control>
+                <NavButton onClick={props.onClick}>
+                    Site list
+                </NavButton>
+            </ControlGroup.Control>
+            <ControlGroup.Description>
+                Enable or disable on listed websites
+            </ControlGroup.Description>
+        </ControlGroup>
+    );
+}
diff --git a/src/ui/popup/site-list-page/index.tsx b/src/ui/popup/site-list-page/index.tsx
new file mode 100644
index 00000000..2802c0c4
--- /dev/null
+++ b/src/ui/popup/site-list-page/index.tsx
@@ -0,0 +1,23 @@
+import {m} from 'malevic';
+import {ViewProps} from '../types';
+import SiteList from './site-list';
+
+export default function SiteListPage(props: ViewProps) {
+    function onSiteListChange(sites: string[]) {
+        props.actions.changeSettings({siteList: sites});
+    }
+
+    const label = props.data.settings.applyToListedOnly ?
+        'Enable on these websites' :
+        'Disable on these websites';
+    return (
+        <div class="site-list-page">
+            <label class="site-list-page__label">{label}</label>
+            <SiteList
+                siteList={props.data.settings.siteList}
+                onChange={onSiteListChange}
+                autoFocus
+            />
+        </div>
+    );
+}
diff --git a/src/ui/popup/site-list-page/site-list.tsx b/src/ui/popup/site-list-page/site-list.tsx
new file mode 100644
index 00000000..e582a382
--- /dev/null
+++ b/src/ui/popup/site-list-page/site-list.tsx
@@ -0,0 +1,93 @@
+import {m} from 'malevic';
+import {getContext} from 'malevic/dom';
+import {Button} from '../../controls';
+
+interface SiteListProps {
+    siteList: string[];
+    autoFocus?: boolean;
+    onChange: (sites: string[]) => void;
+}
+
+export default function SiteList(props: SiteListProps) {
+    const context = getContext();
+    const store = context.store as {prevSiteList: string[]; wasVisible: boolean; editorNode: HTMLTextAreaElement};
+    store.prevSiteList = store.prevSiteList || [];
+    store.wasVisible = store.wasVisible || false;
+
+    const {siteList} = props;
+    const didSiteListChange = (
+        (store.prevSiteList.length !== siteList.length) ||
+        store.prevSiteList.some((s, i) => s !== siteList[i])
+    );
+    store.prevSiteList = siteList;
+
+    function setEditorText() {
+        store.editorNode.value = siteList.join('\n') + '\n';
+    }
+
+    function onEditorRender(el: HTMLTextAreaElement) {
+        store.editorNode = el;
+
+        const isVisible = el.clientHeight > 0;
+        const becameVisible = !store.wasVisible && isVisible;
+
+        if (becameVisible || didSiteListChange) {
+            setEditorText();
+        }
+
+        if (props.autoFocus && becameVisible) {
+            el.selectionStart = el.value.length;
+            el.selectionEnd = el.value.length;
+            el.focus();
+        }
+        store.wasVisible = isVisible;
+    }
+
+    const placeholder = 'www.google.com/maps\nmail.google.com\n';
+
+    function isSiteURLValid(value: string) {
+        return /^([^\.\s]+?\.?)+$/.test(value);
+    }
+
+    function onApplyClick() {
+        let sites = store.editorNode.value
+            .split('\n')
+            .map((site) => site.trim())
+            .filter(isSiteURLValid);
+        sites = Array.from(new Set(sites));
+        props.onChange(sites);
+    }
+
+    function onCancelClick() {
+        setEditorText();
+    }
+
+    return (
+        <div class="site-list">
+            <textarea
+                class="site-list__editor"
+                placeholder={placeholder}
+                onrender={onEditorRender}
+                autocomplete="off"
+                spellcheck="false"
+            ></textarea>
+            <div class="site-list__buttons">
+                <Button
+                    class="site-list__button site-list__button-cancel"
+                    onclick={onCancelClick}
+                >
+                    Cancel
+                </Button>
+                <Button
+                    class="site-list__button site-list__button-apply"
+                    onclick={onApplyClick}
+                >
+                    <span class="site-list__button-apply__content">
+                        <span class="site-list__button-apply__icon"></span>
+                        Apply
+                    </span>
+                </Button>
+            </div>
+        </div>
+    );
+}
diff --git a/src/ui/popup/site-list-page/style.less b/src/ui/popup/site-list-page/style.less
new file mode 100644
index 00000000..1af0921d
--- /dev/null
+++ b/src/ui/popup/site-list-page/style.less
@@ -0,0 +1,64 @@
+@import "../../theme.less";
+
+.site-list-page {
+    align-items: center;
+    display: flex;
+    flex-direction: column;
+    justify-content: stretch;
+}
+
+.site-list {
+    align-items: stretch;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    width: 100%;
+
+    &__editor {
+        background-color: @color-back;
+        border: @size-border solid @color-border;
+        border-radius: 2 * @size-border;
+        box-sizing: border-box;
+        color: @color-input-fore;
+        flex: auto;
+        height: 19rem;
+        outline: none;
+        padding: @indent-small;
+        resize: none;
+
+        &::placeholder {
+            color: @color-input-fore-placeholder;
+        }
+    }
+
+    &__buttons {
+        display: flex;
+        flex-direction: row;
+        margin-top: @indent-small;
+    }
+
+    &__button {
+        border-radius: @size-control-inner / 2 + @size-border;
+        flex: auto;
+    }
+
+    &__button-apply {
+        margin-left: @indent-small;
+
+        &__content {
+            align-items: center;
+            display: inline-flex;
+            justify-content: center;
+        }
+
+        &__icon {
+            background-image: @icon-check;
+            background-position: center;
+            background-repeat: no-repeat;
+            background-size: 0.75rem;
+            display: inline-block;
+            height: 1rem;
+            width: 1rem;
+        }
+    }
+}
diff --git a/src/ui/popup/theme/controls/font-picker.tsx b/src/ui/popup/theme/controls/font-picker.tsx
new file mode 100644
index 00000000..59600351
--- /dev/null
+++ b/src/ui/popup/theme/controls/font-picker.tsx
@@ -0,0 +1,33 @@
+import {m} from 'malevic';
+import {Select} from '../../../controls';
+import ThemeControl from './theme-control';
+import {Theme} from '../../../../definitions';
+
+interface FontPickerProps {
+    theme: Theme;
+    fonts: string[];
+    onChange: (font: string) => void;
+}
+
+export default function FontPicker(props: FontPickerProps) {
+    return (
+        <ThemeControl label="Font name">
+            <Select
+                class={{
+                    'font-picker': true,
+                    'font-picker--disabled': !props.theme.useFont,
+                }}
+                value={props.theme.fontFamily}
+                onChange={props.onChange}
+                options={props.fonts.reduce((map, font) => {
+                    map[font] = (
+                        <div style={{'font-family': font}}>
+                            {font}
+                        </div>
+                    );
+                    return map;
+                }, {} as {[font: string]: Malevic.Spec})}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/index.tsx b/src/ui/popup/theme/controls/index.tsx
index 11712dcb..a8d18c25 100644
--- a/src/ui/popup/theme/controls/index.tsx
+++ b/src/ui/popup/theme/controls/index.tsx
@@ -1,6 +1,7 @@
 import BackgroundColor from './background-color';
 import Brightness from './brightness';
 import Contrast from './contrast';
+import FontPicker from './font-picker';
 import Grayscale from './grayscale';
 import Mode from './mode';
 import ResetButton from './reset-button';
@@ -9,11 +10,14 @@ import Scrollbar from './scrollbar';
 import SelectionColorEditor from './selection';
 import Sepia from './sepia';
 import TextColor from './text-color';
+import TextStroke from './text-stroke';
+import UseFont from './use-font';
 
 export {
     BackgroundColor,
     Brightness,
     Contrast,
+    FontPicker,
     Grayscale,
     Mode,
     ResetButton,
@@ -22,4 +26,6 @@ export {
     SelectionColorEditor,
     Sepia,
     TextColor,
+    TextStroke,
+    UseFont,
 };
diff --git a/src/ui/popup/theme/controls/mode.tsx b/src/ui/popup/theme/controls/mode.tsx
index ed90a5b2..ae60be3c 100644
--- a/src/ui/popup/theme/controls/mode.tsx
+++ b/src/ui/popup/theme/controls/mode.tsx
@@ -17,21 +17,18 @@ export default function Mode(props: {mode: string; onChange: (mode: string) => v
     }
 
     const modes = [
-        [ThemeEngines.dynamicTheme, getLocalMessage('engine_dynamic')],
-        [ThemeEngines.cssFilter, getLocalMessage('engine_filter')],
-        [ThemeEngines.svgFilter, getLocalMessage('engine_filter_plus')],
-        [ThemeEngines.staticTheme, getLocalMessage('engine_static')],
+        {id: ThemeEngines.dynamicTheme, content: getLocalMessage('engine_dynamic')},
+        {id: ThemeEngines.cssFilter, content: getLocalMessage('engine_filter')},
+        {id: ThemeEngines.svgFilter, content: getLocalMessage('engine_filter_plus')},
+        {id: ThemeEngines.staticTheme, content: getLocalMessage('engine_static')},
     ];
     return (
         <ThemeControl label="Mode">
             <div class="mode-control-container">
                 <DropDown
-                    selected={modes.find((m) => m[0] === props.mode)[1]}
-                    values={modes.map((m) => m[1])}
-                    onChange={(v) => {
-                        const mode = modes.find((m) => m[1] === v)[0];
-                        props.onChange(mode);
-                    }}
+                    selected={modes.find((m) => m.id === props.mode).id}
+                    options={modes}
+                    onChange={props.onChange}
                 />
                 <span
                     class={{
diff --git a/src/ui/popup/theme/controls/scheme.tsx b/src/ui/popup/theme/controls/scheme.tsx
index be06dd0e..61a57f11 100644
--- a/src/ui/popup/theme/controls/scheme.tsx
+++ b/src/ui/popup/theme/controls/scheme.tsx
@@ -4,17 +4,15 @@ import {DropDown} from '../../../controls';
 import ThemeControl from './theme-control';
 
 export default function Scheme(props: {isDark: boolean; onChange: (dark: boolean) => void}) {
-    const valDark = getLocalMessage('dark');
-    const valLight = getLocalMessage('light');
     return (
         <ThemeControl label="Scheme">
             <DropDown
-                selected={props.isDark ? valDark : valLight}
-                values={[
-                    valDark,
-                    valLight,
+                selected={props.isDark}
+                options={[
+                    {id: true, content: getLocalMessage('dark')},
+                    {id: false, content: getLocalMessage('light')},
                 ]}
-                onChange={(v) => props.onChange(v === valDark)}
+                onChange={props.onChange}
             />
         </ThemeControl>
     );
diff --git a/src/ui/popup/theme/controls/text-stroke.tsx b/src/ui/popup/theme/controls/text-stroke.tsx
new file mode 100644
index 00000000..dea9b4f8
--- /dev/null
+++ b/src/ui/popup/theme/controls/text-stroke.tsx
@@ -0,0 +1,18 @@
+import {m} from 'malevic';
+import {Slider} from '../../../controls';
+import ThemeControl from './theme-control';
+
+export default function TextStroke(props: {value: number; onChange: (v: number) => void}) {
+    return (
+        <ThemeControl label="Text stroke">
+            <Slider
+                value={props.value}
+                min={0}
+                max={1}
+                step={0.1}
+                formatValue={String}
+                onChange={props.onChange}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/controls/use-font.tsx b/src/ui/popup/theme/controls/use-font.tsx
new file mode 100644
index 00000000..bc6b998e
--- /dev/null
+++ b/src/ui/popup/theme/controls/use-font.tsx
@@ -0,0 +1,16 @@
+import {m} from 'malevic';
+import {DropDown} from '../../../controls';
+import ThemeControl from './theme-control';
+
+export default function UseFont(props: {value: boolean; onChange: (boolean: any) => void}) {
+    const options = [{id: true, content: 'Yes'}, {id: false, content: 'No'}];
+    return (
+        <ThemeControl label="Change font">
+            <DropDown
+                options={options}
+                onChange={props.onChange}
+                selected={props.value}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/page/index.tsx b/src/ui/popup/theme/page/index.tsx
index b66581ac..d61666d9 100644
--- a/src/ui/popup/theme/page/index.tsx
+++ b/src/ui/popup/theme/page/index.tsx
@@ -2,7 +2,7 @@ import {m} from 'malevic';
 import {DEFAULT_SETTINGS, DEFAULT_THEME, DEFAULT_COLORS} from '../../../../defaults';
 import {Theme} from '../../../../definitions';
 import {ViewProps} from '../../types';
-import {BackgroundColor, Brightness, Contrast, Grayscale, Mode, ResetButton, Scheme, Scrollbar, SelectionColorEditor, Sepia, TextColor} from '../controls';
+import {BackgroundColor, Brightness, Contrast, FontPicker, Grayscale, Mode, ResetButton, Scheme, Scrollbar, SelectionColorEditor, Sepia, TextColor, TextStroke, UseFont} from '../controls';
 import ThemePresetPicker from '../preset-picker';
 import {getCurrentThemePreset} from '../utils';
 import Collapsible from './collapsible-panel';
@@ -78,6 +78,30 @@ function ColorsGroup({theme, change}: ThemeGroupProps) {
     );
 }
 
+interface FontGroupsProps extends ThemeGroupProps {
+    fonts: string[];
+}
+
+function FontGroup({theme, fonts, change}: FontGroupsProps) {
+    return (
+        <Array>
+            <UseFont
+                value={theme.useFont}
+                onChange={(useFont) => change({useFont})}
+            />
+            <FontPicker
+                theme={theme}
+                fonts={fonts}
+                onChange={(fontFamily) => change({fontFamily})}
+            />
+            <TextStroke
+                value={theme.textStroke}
+                onChange={(textStroke) => change({textStroke})}
+            />
+        </Array>
+    );
+}
+
 export default function ThemePage(props: ViewProps) {
     const {theme, change} = getCurrentThemePreset(props);
 
@@ -91,6 +115,9 @@ export default function ThemePage(props: ViewProps) {
                 <Collapsible.Group id="colors" label="Colors">
                     <ColorsGroup theme={theme} change={change} />
                 </Collapsible.Group>
+                <Collapsible.Group id="font" label="Font, text stroke">
+                    <FontGroup theme={theme} fonts={props.data.fonts} change={change} />
+                </Collapsible.Group>
             </Collapsible>
             <ResetButton {...props} />
         </section>
diff --git a/src/ui/popup/theme/page/style.less b/src/ui/popup/theme/page/style.less
index 91cc86e9..7b150534 100644
--- a/src/ui/popup/theme/page/style.less
+++ b/src/ui/popup/theme/page/style.less
@@ -3,22 +3,26 @@
 
 .theme-page {
     .theme-preset-picker.dropdown {
-        border-radius: unset;
+        border-radius: @size-control-inner / 2 + @size-border;
         height: @size-control-inner + 2 * @size-border;
     }
 
     .theme-preset-picker {
         .dropdown {
             &__selected {
-                border-radius: unset;
+                border-radius: @size-control-inner / 2 + @size-border;
                 height: @size-control-inner + 2 * @size-border;
-                line-height: @size-control-inner + 2 * @size-border;
             }
         
             &__list {
+                border: @size-border solid @color-border;
+                border-top: none;
+                border-radius: @size-control-inner / 2 + @size-border;
+                box-shadow: 0 0 0.25rem 0.0625rem black;
+                box-sizing: border-box;
+
                 &__item {
-                    height: @size-control-inner + 2 * @size-border;
-                    line-height: @size-control-inner + 2 * @size-border;
+                    min-height: @size-control-inner + 2 * @size-border;
                 }
             }
         }
@@ -27,4 +31,126 @@
     .theme-control:not(:first-child) {
         margin-top: @indent-small;
     }
+
+    .font-picker {
+        box-sizing: border-box;
+        height: 1.5rem;
+        padding-top: 0.125rem;
+        position: relative;
+        width: 100%;
+
+        &--disabled {
+            opacity: 0.5;
+            pointer-events: none;
+        }
+
+        .select {
+            &__textbox,
+            &__expand,
+            &__list {
+                border: none;
+            }
+
+            &__line {
+                position: relative;
+            }
+
+            &__textbox {
+                background-color: @color-control-active;
+                border-radius: 0.625rem;
+                color: @color-control-fore;
+                height: 1.25rem;
+                line-height: 1.25rem;
+                text-align: center;
+                width: 100%;
+
+                &:hover {
+                    background-color: @color-input-hover;
+                }
+
+                &:focus {
+                    background-color: @color-input-hover;
+                    border: @size-border solid @color-border;
+                    border-radius: 0.75rem;
+                    margin-top: -0.125rem;
+                }
+            }
+
+            &__expand {
+                background-color: @color-control-active;
+                border-radius: 0.625rem;
+                height: 1.25rem;
+                min-width: 1.5rem;
+                position: absolute;
+                right: 0;
+                top: 0;
+                width: 1.5rem;
+
+                &__icon {
+                    background-image: @icon-down;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                    background-size: 1rem;
+
+                    &::before,
+                    &::after {
+                        display: none;
+                    }
+                }
+            }
+
+            &__list {
+                border-bottom-left-radius: 0.625rem;
+                border-bottom-right-radius: 0.625rem;
+                border-left: @size-border solid @color-border;
+                border-right: @size-border solid @color-border;
+                top: 100%;
+                width: 100%;
+
+                &--expanded {
+                    border-bottom: @size-border solid @color-border;
+                }
+
+                &::-webkit-scrollbar {
+                    background-color: transparent;
+                }
+
+                &::-webkit-scrollbar-thumb {
+                    border-radius: 0.375rem;
+                }
+            }
+        }
+
+        &.select--expanded {
+            background-color: @color-border;
+            border-top-left-radius: 0.75rem;
+            border-top-right-radius: 0.75rem;
+            height: 1.5rem;
+            padding-top: 0;
+
+            .select__textbox {
+                border: @size-border solid @color-border;
+                border-radius: 0.75rem;
+
+                &:focus {
+                    margin-top: 0;
+                }
+            }
+
+            .select__expand {
+                margin-top: @size-border;
+            }
+
+            &::before {
+                background-color: @color-back;
+                content: "";
+                display: inline-block;
+                height: 0.75rem;
+                left: @size-border;
+                position: absolute;
+                top: 0.75rem;
+                width: calc(100% - 2 * @size-border);
+            }
+        }
+    }
 }
diff --git a/src/ui/popup/theme/preset-picker/index.tsx b/src/ui/popup/theme/preset-picker/index.tsx
index cd2f75eb..6de19fb4 100644
--- a/src/ui/popup/theme/preset-picker/index.tsx
+++ b/src/ui/popup/theme/preset-picker/index.tsx
@@ -1,37 +1,150 @@
 import {m} from 'malevic';
-import {isURLInList, getURLHost} from '../../../../utils/url';
-import {DropDown} from '../../../controls';
+import {getContext} from 'malevic/dom';
+import {ThemePreset} from '../../../../definitions';
+import {isURLInList, isURLMatched, getURLHostOrProtocol} from '../../../../utils/url';
+import {DropDown, MessageBox} from '../../../controls';
 import {ViewProps} from '../../types';
+import {generateUID} from '../../../../utils/uid';
+
+function PresetItem(props: ViewProps & {preset: ThemePreset}) {
+    const context = getContext();
+    const store = context.store as {isConfirmationVisible: boolean};
+
+    function onRemoveClick(e: MouseEvent) {
+        e.stopPropagation();
+        store.isConfirmationVisible = true;
+        context.refresh();
+    }
+
+    function onConfirmRemoveClick() {
+        const filtered = props.data.settings.presets.filter((p) => p.id !== props.preset.id);
+        props.actions.changeSettings({presets: filtered});
+    }
+
+    function onCancelRemoveClick() {
+        store.isConfirmationVisible = false;
+        context.refresh();
+    }
+
+    const confirmation = store.isConfirmationVisible ? (
+        <MessageBox
+            caption={`Are you sure you want to remove ${props.preset.name}?`}
+            onOK={onConfirmRemoveClick}
+            onCancel={onCancelRemoveClick}
+        />
+    ) : null;
+
+    return (
+        <span class="theme-preset-picker__preset">
+            <span class="theme-preset-picker__preset__name">{props.preset.name}</span>
+            <span class="theme-preset-picker__preset__remove-button" onclick={onRemoveClick}></span>
+            {confirmation}
+        </span>
+    );
+}
+
+const MAX_ALLOWED_PRESETS = 3;
 
 export default function PresetPicker(props: ViewProps) {
-    const host = getURLHost(props.tab.url || '');
+    const host = getURLHostOrProtocol(props.tab.url);
+    const preset = props.data.settings.presets.find(
+        ({urls}) => isURLInList(props.tab.url, urls)
+    );
     const custom = props.data.settings.customThemes.find(
         ({url}) => isURLInList(props.tab.url, url)
     );
 
-    const defaultPresetName = 'Default theme';
-    const customPresetName = `Custom for ${host}`;
+    const selectedPresetId = custom ? 'custom' : preset ? preset.id : 'default';
+
+    const defaultOption = {id: 'default', content: 'Default theme'};
+    const addNewPresetOption = props.data.settings.presets.length < MAX_ALLOWED_PRESETS ?
+        {id: 'add-preset', content: '\uff0b Create new theme'} :
+        null;
+    const userPresetsOptions = props.data.settings.presets.map((preset) => {
+        if (preset.id === selectedPresetId) {
+            return {id: preset.id, content: preset.name};
+        }
+        return {
+            id: preset.id,
+            content: <PresetItem {...props} preset={preset} />
+        };
+    });
+    const customSitePresetOption = {
+        id: 'custom',
+        content: `${selectedPresetId === 'custom' ? '\u2605' : '\u2606'} Custom for ${host}`,
+    };
+
+    const dropdownOptions = [
+        defaultOption,
+        ...userPresetsOptions,
+        addNewPresetOption,
+        customSitePresetOption,
+    ].filter(Boolean);
 
-    function onPresetChange(name: string) {
+    function onPresetChange(id: string) {
         const filteredCustomThemes = props.data.settings.customThemes.filter(({url}) => !isURLInList(props.tab.url, url));
-        if (name === defaultPresetName) {
-            props.actions.changeSettings({customThemes: filteredCustomThemes});
-        } else if (name === customPresetName) {
+        const filteredPresets = props.data.settings.presets.map((preset) => {
+            return {
+                ...preset,
+                urls: preset.urls.filter((template) => !isURLMatched(props.tab.url, template)),
+            };
+        });
+        if (id === 'default') {
+            props.actions.changeSettings({
+                customThemes: filteredCustomThemes,
+                presets: filteredPresets,
+            });
+        } else if (id === 'custom') {
             const extended = filteredCustomThemes.concat({
                 url: [host],
                 theme: {...props.data.settings.theme},
             });
-            props.actions.changeSettings({customThemes: extended});
+            props.actions.changeSettings({
+                customThemes: extended,
+                presets: filteredPresets,
+            });
+        } else if (id === 'add-preset') {
+            let newPresetName: string;
+            for (let i = 0; i <= props.data.settings.presets.length; i++) {
+                newPresetName = `Theme ${i + 1}`;
+                if (props.data.settings.presets.every((p) => p.name !== newPresetName)) {
+                    break;
+                }
+            }
+
+            const extended = filteredPresets.concat({
+                id: `preset-${generateUID()}`,
+                name: newPresetName,
+                urls: [host],
+                theme: {...props.data.settings.theme},
+            });
+            props.actions.changeSettings({
+                customThemes: filteredCustomThemes,
+                presets: extended,
+            });
+        } else {
+            const chosenPresetId = id;
+            const extended = filteredPresets.map((preset) => {
+                if (preset.id === chosenPresetId) {
+                    return {
+                        ...preset,
+                        urls: preset.urls.concat(host)
+                    };
+                }
+                return preset;
+            });
+            props.actions.changeSettings({
+                customThemes: filteredCustomThemes,
+                presets: extended,
+            });
         }
     }
+
     return (
         <DropDown
             class="theme-preset-picker"
-            selected={custom ? customPresetName : defaultPresetName}
-            values={[
-                defaultPresetName,
-                customPresetName,
-            ]}
+            selected={selectedPresetId}
+            options={dropdownOptions}
             onChange={onPresetChange}
         />
     );
diff --git a/src/ui/popup/theme/preset-picker/style.less b/src/ui/popup/theme/preset-picker/style.less
index ac0d25f1..37fd3bf7 100644
--- a/src/ui/popup/theme/preset-picker/style.less
+++ b/src/ui/popup/theme/preset-picker/style.less
@@ -1,13 +1,54 @@
+@import "../../../theme";
+
+@theme-preset-height: 1.5rem;
+@theme-preset-picker-button-size: 1.5rem;
+
 .theme-preset-picker {
+    border-radius: @theme-preset-height / 2;
+
     .dropdown {
         &__selected {
-            border-radius: unset;
+            border-radius: @theme-preset-height / 2;
             height: @size-control-inner;
-            line-height: @size-control-inner;
             margin-top: 0;
         }
+
         &__list {
-            border-radius: unset;
+            border: none;
+            border-radius: @theme-preset-height / 2;
+            box-shadow: 0 0 0 @size-border @color-border, 0 0 0.25rem 0.0625rem + @size-border black;
+        }
+    }
+
+    &__preset {
+        align-items: center;
+        display: inline-flex;
+        justify-content: center;
+        min-height: @theme-preset-height;
+        position: relative;
+        width: 100%;
+
+        &__remove-button {
+            border-radius: 50%;
+            cursor: pointer;
+            display: inline-block;
+            height: @theme-preset-picker-button-size;
+            position: absolute;
+            right: 0;
+            top: 0;
+            width: @theme-preset-picker-button-size;
+
+            &::after {
+                content: "\2715";
+                display: inline-block;
+                line-height: @theme-preset-picker-button-size;
+                text-align: center;
+            }
+
+            &:hover {
+                background-color: @color-control-active;
+                color: @color-control-fore;
+            }
         }
     }
 }
diff --git a/src/ui/popup/theme/utils.ts b/src/ui/popup/theme/utils.ts
index 676666cf..908441be 100644
--- a/src/ui/popup/theme/utils.ts
+++ b/src/ui/popup/theme/utils.ts
@@ -6,9 +6,14 @@ export function getCurrentThemePreset(props: ViewProps) {
     const custom = props.data.settings.customThemes.find(
         ({url}) => isURLInList(props.tab.url, url)
     );
+    const preset = custom ? null : props.data.settings.presets.find(
+        ({urls}) => isURLInList(props.tab.url, urls)
+    );
     const theme = custom ?
         custom.theme :
-        props.data.settings.theme;
+        preset ?
+            preset.theme :
+            props.data.settings.theme;
 
     function setTheme(config: Partial<Theme>) {
         if (custom) {
@@ -16,6 +21,11 @@ export function getCurrentThemePreset(props: ViewProps) {
             props.actions.changeSettings({
                 customThemes: props.data.settings.customThemes,
             });
+        } else if (preset) {
+            preset.theme = {...preset.theme, ...config};
+            props.actions.changeSettings({
+                presets: props.data.settings.presets,
+            });
         } else {
             props.actions.setTheme(config);
         }
diff --git a/src/ui/stylesheet-editor/components/body.tsx b/src/ui/stylesheet-editor/components/body.tsx
index e4592adc..cb7b8ea2 100644
--- a/src/ui/stylesheet-editor/components/body.tsx
+++ b/src/ui/stylesheet-editor/components/body.tsx
@@ -1,6 +1,6 @@
 import {m} from 'malevic';
 import {Button} from '../../controls';
-import {getURLHost, isURLInList} from '../../../utils/url';
+import {getURLHostOrProtocol, isURLInList} from '../../../utils/url';
 import {ExtWrapper, TabInfo} from '../../../definitions';
 
 interface BodyProps extends ExtWrapper {
@@ -9,7 +9,7 @@ interface BodyProps extends ExtWrapper {
 
 export default function Body({data, tab, actions}: BodyProps) {
 
-    const host = getURLHost(tab.url);
+    const host = getURLHostOrProtocol(tab.url);
     const custom = data.settings.customThemes.find(({url}) => isURLInList(tab.url, url));
 
     let textNode: HTMLTextAreaElement;
diff --git a/src/ui/theme.less b/src/ui/theme.less
index 777c0c52..23775f8f 100644
--- a/src/ui/theme.less
+++ b/src/ui/theme.less
@@ -17,6 +17,8 @@
 @color-border: #316e7d;
 @color-heading: #e96c4c;
 @color-error: #db4245;
+@color-ok: #317c4e;
+@color-ok-fore: #3bc077;
 @color-selection-back: #e96c4c;
 @color-selection-fore: #ffffff;
 
diff --git a/src/utils/text.ts b/src/utils/text.ts
index 5c39a41d..1c063cac 100644
--- a/src/utils/text.ts
+++ b/src/utils/text.ts
@@ -60,3 +60,47 @@ export function getMatches(regex: RegExp, input: string, group = 0) {
 export function getStringSize(value: string) {
     return value.length * 2;
 }
+
+export function formatCSS(text: string) {
+
+    function trimLeft(text: string) {
+        return text.replace(/^\s+/, '');
+    }
+
+    function getIndent(depth: number) {
+        if (depth === 0) {
+            return '';
+        }
+        return ' '.repeat(4 * depth);
+    }
+
+    const emptyRuleRegexp = /[^{}]+{\s*}/g;
+    while (emptyRuleRegexp.test(text)) {
+        text = text.replace(emptyRuleRegexp, '');
+    }
+
+    const css = (text
+        .replace(/\s{2,}/g, ' ') // Replacing multiple spaces to one
+        .replace(/\{/g, '{\n') // {
+        .replace(/\}/g, '\n}\n') // }
+        .replace(/\;(?![^(\(|\")]*(\)|\"))/g, ';\n') // ; and do not target between () and ""
+        .replace(/\,(?![^(\(|\")]*(\)|\"))/g, ',\n') // , and do not target between () and ""
+        .replace(/\n\s*\n/g, '\n') // Remove \n Without any characters between it to the next \n
+        .split('\n'));
+
+    let depth = 0;
+    const formatted = [];
+
+    for (let x = 0, len = css.length; x < len; x++) {
+        const line = css[x] + '\n';
+        if (line.match(/\{/)) { // {
+            formatted.push(getIndent(depth++) + trimLeft(line));
+        } else if (line.match(/\}/)) { // }
+            formatted.push(getIndent(--depth) + trimLeft(line));
+        } else { // CSS line
+            formatted.push(getIndent(depth) + trimLeft(line));
+        }
+    }
+
+    return formatted.join('').trim();
+}
diff --git a/src/utils/uid.ts b/src/utils/uid.ts
new file mode 100644
index 00000000..56c2aadc
--- /dev/null
+++ b/src/utils/uid.ts
@@ -0,0 +1,7 @@
+function hexify(number: number) {
+    return ((number < 16 ? '0' : '') + number.toString(16));
+}
+
+export function generateUID() {
+    return Array.from(crypto.getRandomValues(new Uint8Array(16))).map((x) => hexify(x)).join('');
+}
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 7f8590d9..fbae5101 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -1,8 +1,13 @@
 import {UserSettings} from '../definitions';
 import {isIPV6, compareIPV6} from './ipv6';
 
-export function getURLHost(url: string) {
-    return url.match(/^(.*?\/{2,3})?(.+?)(\/|$)/)[2];
+export function getURLHostOrProtocol($url: string) {
+    const url = new URL($url);
+    if (url.host) {
+        return url.host;
+    } else {
+        return url.protocol;
+    }
 }
 
 export function compareURLPatterns(a: string, b: string) {


Test patch:
diff --git a/tests/config/config.tests.ts b/tests/config/config.tests.ts
index 0d4aba2e..12c1bf26 100644
--- a/tests/config/config.tests.ts
+++ b/tests/config/config.tests.ts
@@ -62,7 +62,7 @@ test('Dynamic Theme Fixes config', async () => {
     expect(fixes.map(({url}) => url[0])).toEqual(fixes.map(({url}) => url[0]).sort(compareURLPatterns));
 
     // selectors should have no comma
-    expect(fixes.every(({invert, ignoreInlineStyle}) => (invert || []).concat(ignoreInlineStyle || []).every((s) => s.indexOf(',') < 0))).toBe(true);
+    expect(fixes.every(({invert, ignoreInlineStyle, ignoreImageAnalysis}) => (invert || []).concat(ignoreInlineStyle || []).concat(ignoreImageAnalysis || []).every((s) => s.indexOf(',') < 0))).toBe(true);
 
     // fixes are properly formatted
     expect(throwIfDifferent(file, formatDynamicThemeFixes(fixes), 'Dynamic fixes format error')).not.toThrow();
@@ -84,10 +84,14 @@ test('Dynamic Theme Fixes config', async () => {
         '========',
         'wikipedia.org',
         'IGNORE INLINE STYLE', 'a', 'b',
+        '========',
+        'duckduckgo.com',
+        'IGNORE IMAGE ANALYSIS', 'img[alt="Logo"]', 'canvas',
     ].join('\r\n'))).toEqual([
         {url: ['inbox.google.com', 'mail.google.com'], invert: ['a', 'b'], css: '.x { color: white !important; }'},
         {url: ['twitter.com'], invert: ['c', 'd']},
         {url: ['wikipedia.org'], ignoreInlineStyle: ['a', 'b']},
+        {url: ['duckduckgo.com'], ignoreImageAnalysis: ['img[alt="Logo"]', 'canvas']},
     ]);
 });
 
diff --git a/tests/utils/text.tests.ts b/tests/utils/text.tests.ts
new file mode 100644
index 00000000..d51cfb34
--- /dev/null
+++ b/tests/utils/text.tests.ts
@@ -0,0 +1,101 @@
+import {formatCSS} from '../../src/utils/text';
+
+test('CSS formatting', () => {
+    expect(formatCSS('div { color: red; }'))
+        .toEqual([
+            'div {',
+            '    color: red;',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('div { color: red; } .list-item { background: rgb(0, 0, 0); color: white; }'))
+        .toEqual([
+            'div {',
+            '    color: red;',
+            '}',
+            '.list-item {',
+            '    background: rgb(0, 0, 0);',
+            '    color: white;',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('@media screen { div { color: red; } span { color: red; } } @media all { p { color: green; } }'))
+        .toEqual([
+            '@media screen {',
+            '    div {',
+            '        color: red;',
+            '    }',
+            '    span {',
+            '        color: red;',
+            '    }',
+            '}',
+            '@media all {',
+            '    p {',
+            '        color: green;',
+            '    }',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('div, span { background: green; color: red; }'))
+        .toEqual([
+            'div,',
+            'span {',
+            '    background: green;',
+            '    color: red;',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('@media print, screen and (min-width: 20rem) { div, span { background: green; color: red; } }'))
+        .toEqual([
+            '@media print,',
+            'screen and (min-width: 20rem) {',
+            '    div,',
+            '    span {',
+            '        background: green;',
+            '        color: red;',
+            '    }',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('.icon { background-image: url(data:image/gif;base64,XYZ); }'))
+        .toEqual([
+            '.icon {',
+            '    background-image: url(data:image/gif;base64,XYZ);',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('img[src*="a,b;c"] { filter: invert(1); }'))
+        .toEqual([
+            'img[src*="a,b;c"] {',
+            '    filter: invert(1);',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('img[src*="a,b;c"] {\n    filter: invert(1); \n}'))
+        .toEqual([
+            'img[src*="a,b;c"] {',
+            '    filter: invert(1);',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('div { } span { color: red; } button { }'))
+        .toEqual([
+            'span {',
+            '    color: red;',
+            '}',
+        ].join('\n'));
+
+    expect(formatCSS('@media all { div { } span { color: red; } } @media all { div { } } @media all { button { color: green; } }'))
+        .toEqual([
+            '@media all {',
+            '    span {',
+            '        color: red;',
+            '    }',
+            '}',
+            '@media all {',
+            '    button {',
+            '        color: green;',
+            '    }',
+            '}',
+        ].join('\n'));
+});
diff --git a/tests/utils/url.tests.ts b/tests/utils/url.tests.ts
index bc627c9e..a901737f 100644
--- a/tests/utils/url.tests.ts
+++ b/tests/utils/url.tests.ts
@@ -1,4 +1,4 @@
-import {isURLEnabled, isURLMatched, isPDF} from '../../src/utils/url';
+import {isURLEnabled, isURLMatched, isPDF, getURLHostOrProtocol} from '../../src/utils/url';
 import {UserSettings} from '../../src/definitions';
 
 test('URL is enabled', () => {
@@ -211,3 +211,13 @@ test('URL is enabled', () => {
         {isProtected: false, isInDarkList: false},
     )).toBe(true);
 });
+
+test('Get URL host or protocol', () => {
+    expect(getURLHostOrProtocol('https://www.google.com')).toBe('www.google.com');
+    expect(getURLHostOrProtocol('https://www.google.com/maps')).toBe('www.google.com');
+    expect(getURLHostOrProtocol('http://localhost:8080')).toBe('localhost:8080');
+    expect(getURLHostOrProtocol('about:blank')).toBe('about:');
+    expect(getURLHostOrProtocol('http://user:pass@www.example.org')).toBe('www.example.org');
+    expect(getURLHostOrProtocol('data:text/html,<html>Hello</html>')).toBe('data:');
+    expect(getURLHostOrProtocol('file:///Users/index.html')).toBe('file:');
+});