├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── .stylelintrc.json ├── LICENSE.md ├── README.md ├── build ├── build.js ├── files.js ├── medium-sites.js ├── settings.json ├── template.user.css └── usercss.js ├── darker-medium-extras.user.css ├── darker-medium.user.css ├── gist.css ├── icons ├── icon128.png ├── icon16.png ├── icon19.png ├── icon32.png ├── icon38.png └── icon48.png ├── images ├── Darker-Medium-logo.svg ├── Darker-Medium-options.png ├── Darker-Medium-toggle.gif └── Medium-site-screenshot.png ├── js ├── background.js ├── content.js ├── run_prettify.js └── settings.js ├── manifest.json ├── options ├── options.css ├── options.html └── options.js ├── package.json ├── style-profiles.css └── style.css /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | insert_final_newline = true 11 | max_line_length = 80 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | js/run_prettify.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | # https://github.com/eslint/eslint/blob/master/docs/rules/README.md 2 | 3 | globals: 4 | chrome: true 5 | 6 | env: 7 | es6: true 8 | browser: true 9 | 10 | rules: 11 | accessor-pairs: [2] 12 | array-bracket-spacing: [2, never] 13 | array-callback-return: [0] 14 | arrow-body-style: [2, as-needed] 15 | arrow-parens: [2, as-needed] 16 | arrow-spacing: [2, {before: true, after: true}] 17 | block-scoped-var: [2] 18 | brace-style: [2, 1tbs, {allowSingleLine: true}] 19 | camelcase: [2, {properties: never}] 20 | comma-dangle: [0] 21 | comma-spacing: [2, {before: false, after: true}] 22 | comma-style: [2, last] 23 | complexity: [0] 24 | computed-property-spacing: [2, never] 25 | consistent-return: [0] 26 | constructor-super: [2] 27 | curly: [0] 28 | default-case: [0] 29 | dot-location: [2, property] 30 | dot-notation: [0] 31 | eol-last: [2] 32 | eqeqeq: [2] 33 | generator-star-spacing: [2, before] 34 | global-require: [0] 35 | guard-for-in: [2] 36 | handle-callback-err: [2, ^(err|error)$] 37 | id-blacklist: [0] 38 | indent: [0, 2] 39 | jsx-quotes: [0] 40 | key-spacing: [0] 41 | keyword-spacing: [2] 42 | linebreak-style: [2, unix] 43 | lines-around-comment: [0] 44 | max-nested-callbacks: [0] 45 | new-cap: [0] 46 | new-parens: [2] 47 | newline-per-chained-call: [0] 48 | no-alert: [0] 49 | no-array-constructor: [0] 50 | no-bitwise: [0] 51 | no-caller: [2] 52 | no-case-declarations: [2] 53 | no-class-assign: [2] 54 | no-cond-assign: [2, except-parens] 55 | no-confusing-arrow: [2] 56 | no-const-assign: [2] 57 | no-constant-condition: [0] 58 | no-control-regex: [2] 59 | no-debugger: [2] 60 | no-delete-var: [2] 61 | no-div-regex: [0] 62 | no-dupe-args: [2] 63 | no-dupe-class-members: [2] 64 | no-dupe-keys: [2] 65 | no-duplicate-case: [2] 66 | no-else-return: [0] 67 | no-empty-character-class: [2] 68 | no-empty-function: [0] 69 | no-empty-pattern: [2] 70 | no-empty: [0] 71 | no-eq-null: [2] 72 | no-eval: [2] 73 | no-ex-assign: [2] 74 | no-extend-native: [2] 75 | no-extra-bind: [2] 76 | no-extra-boolean-cast: [2] 77 | no-extra-label: [0] 78 | no-extra-parens: [0] 79 | no-extra-semi: [2] 80 | no-fallthrough: [2] 81 | no-floating-decimal: [0] 82 | no-func-assign: [2] 83 | no-implicit-coercion: [0] 84 | no-implicit-globals: [0] 85 | no-implied-eval: [2] 86 | no-inline-comments: [0] 87 | no-inner-declarations: [2] 88 | no-invalid-regexp: [2] 89 | no-invalid-this: [0] 90 | no-irregular-whitespace: [2] 91 | no-iterator: [2] 92 | no-label-var: [2] 93 | no-labels: [2] 94 | no-lone-blocks: [2] 95 | no-lonely-if: [0] 96 | no-loop-func: [0] 97 | no-magic-numbers: [0] 98 | no-mixed-requires: [0] 99 | no-mixed-spaces-and-tabs: [2] 100 | no-multi-spaces: [0] 101 | no-multi-str: [2] 102 | no-multiple-empty-lines: [2, {max: 1}] 103 | no-native-reassign: [2] 104 | no-negated-condition: [0] 105 | no-negated-in-lhs: [2] 106 | no-nested-ternary: [0] 107 | no-new-func: [2] 108 | no-new-object: [2] 109 | no-new-require: [2] 110 | no-new-symbol: [2] 111 | no-new-wrappers: [2] 112 | no-new: [0] 113 | no-obj-calls: [2] 114 | no-octal-escape: [2] 115 | no-octal: [2] 116 | no-path-concat: [2] 117 | no-process-exit: [0] 118 | no-proto: [2] 119 | no-redeclare: [2] 120 | no-regex-spaces: [2] 121 | no-restricted-imports: [0] 122 | no-restricted-modules: [2, domain, freelist, smalloc, sys] 123 | no-restricted-syntax: [2, WithStatement] 124 | no-return-assign: [2, except-parens] 125 | no-script-url: [2] 126 | no-self-assign: [2] 127 | no-self-compare: [2] 128 | no-sequences: [2] 129 | no-shadow-restricted-names: [2] 130 | no-shadow: [0] 131 | no-spaced-func: [2] 132 | no-sparse-arrays: [2] 133 | no-this-before-super: [2] 134 | no-throw-literal: [2] 135 | no-trailing-spaces: [2] 136 | no-undef-init: [2] 137 | no-undef: [2] 138 | no-undefined: [0] 139 | no-underscore-dangle: [0] 140 | no-unexpected-multiline: [2] 141 | no-unmodified-loop-condition: [2] 142 | no-unneeded-ternary: [2] 143 | no-unreachable: [2] 144 | no-unused-expressions: [2] 145 | no-unused-labels: [0] 146 | no-unused-vars: [2, {args: all, argsIgnorePattern: ^_}] 147 | no-use-before-define: [2, nofunc] 148 | no-useless-call: [2] 149 | no-useless-concat: [2] 150 | no-useless-constructor: [2] 151 | no-warning-comments: [0] 152 | no-whitespace-before-property: [2] 153 | no-with: [2] 154 | object-curly-spacing: [2, never] 155 | one-var-declaration-per-line: [0] 156 | one-var: [0] 157 | operator-assignment: [2, always] 158 | operator-linebreak: [2, after] 159 | padded-blocks: [2, never] 160 | prefer-rest-params: [0] 161 | quote-props: [0] 162 | quotes: [2, double] 163 | radix: [2, always] 164 | semi-spacing: [2, {before: false, after: true}] 165 | semi: [2, always] 166 | sort-imports: [0] 167 | space-before-blocks: [2, always] 168 | space-before-function-paren: [2, never] 169 | space-in-parens: [2, never] 170 | space-infix-ops: [2] 171 | space-unary-ops: [2] 172 | spaced-comment: [2, always, {markers: ["!"]}] 173 | strict: [0] 174 | template-curly-spacing: [2, never] 175 | use-isnan: [2] 176 | valid-typeof: [2] 177 | wrap-iife: [2, inside] 178 | yield-star-spacing: [2, {before: true, after: false}] 179 | yoda: [2, never] 180 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | *.md diss=astextplain 24 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at wowmotty@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. [Getting Involved](#getting-involved) 4 | 3. [Getting Started](#getting-started) 5 | 4. [Build & Test](#build--test) 6 | 7 | ## Getting Involved 8 | 9 | There are a number of ways to get involved with the development of this Web Extension. Even if you've never contributed to an Open Source project before, we're always looking for help identifying spelling mistakes, grammar corrections or any other kind of help. 10 | 11 | **This project is bound by a [Code of Conduct](CODE_OF_CONDUCT.md)**. 12 | 13 | ## Getting Started 14 | 15 | Here is a quick summary: 16 | 17 | * Download, fork or clone this repository to your computer. 18 | * Make sure [node.js](http://nodejs.org/) is installed. 19 | * Run `npm install` to get the build scripts working. 20 | * Create and switch to a branch before making any corrections or enhancements to the files. 21 | * Run `npm test` to check the linting of both the JavaScript and CSS files. 22 | * Commit and push the changes to your fork. 23 | * Submit a pull request with a description of the changes you made. 24 | 25 | For more details, check out this [contributing guide](https://github.com/Roshanjossey/first-contributions#readme). 26 | 27 | ## Build & test 28 | 29 | * If adding an unsupported domain for the usercss style, please modify the [`build/medium-sites.js` file](https://github.com/Mottie/Darker-Medium/blob/master/build/medium-sites.js). 30 | * To test this script, make sure to run `npm install` to install the required node modules. 31 | * Note that at the time of this writing, you will see some warnings while attempting to load the unpacked extension into Chrome: 32 | * The warnings are related to the [`node-jsonwebtoken`](https://github.com/auth0/node-jsonwebtoken/issues/411) module. 33 | * It is safe to ignore these warnings as they will not impede this extension from running. 34 | * Currently there are no unit tests associated with this web extension, so running `npm test` will only check for linting errors. 35 | * You can test your changes by loading the extension into your browser 36 | 37 | ### Webkit (Chrome & Opera) 38 | 39 | * Go to the Menu > More tools > Extensions. 40 | * Check the "Developer mode" checkbox. 41 | * Click on "Load unpacked extension...". 42 | * Find the folder for this extension on your computer. 43 | * Click on the "background page" link next to "Inspect views" to check for errors on the background page. 44 | * Open the options, then right click and "Inspect" to open the Development tools to check for errors. 45 | 46 | ### Firefox 47 | 48 | * First, you'll need to create the compressed `.xpi` file: 49 | * Make sure to run `npm install` first, if you haven't done it already. 50 | * Now use `npm run build` to create the `darker-medium.xpi` file in the root directory. 51 | * Open Firefox 52 | * Navigate to `about:debugging#addons`. 53 | * Click "Load Temporary Add-on". 54 | * *Make sure* to select the `darker-medium.xpi` file. 55 | * Click "Debug", then "OK" when the pop up asks to permit a remote debugging connection; This is the browser communicating with the debug window, it has nothing to do with this web extension. 56 | * Check for errors in this debugging window. 57 | * Please be aware that not all the errors shown in the debug window are related to this web extension. 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | * **Browser**: 11 | * **Operating System**: 12 | * **URL of the problem page**: 13 | 14 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # lockfiles 2 | package-lock.json 3 | yarn.lock 4 | 5 | # temp stuff 6 | tmp/ 7 | *.tmp 8 | *.bak 9 | 10 | # logs 11 | *.stackdump 12 | *.log 13 | 14 | # Build 15 | node_modules/ 16 | 17 | # Built Web Extension files 18 | *.xpi 19 | *.zip 20 | 21 | # Windows crap 22 | Thumbs.db 23 | Desktop.ini 24 | 25 | # Mac crap 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "at-rule-empty-line-before": null, 4 | "at-rule-name-case": "lower", 5 | "at-rule-name-space-after": "always-single-line", 6 | "at-rule-semicolon-newline-after": "always", 7 | "block-closing-brace-empty-line-before": "never", 8 | "block-closing-brace-newline-after": "always", 9 | "block-closing-brace-newline-before": "always-multi-line", 10 | "block-closing-brace-space-before": "always-single-line", 11 | "block-no-empty": null, 12 | "block-opening-brace-newline-after": "always-multi-line", 13 | "block-opening-brace-space-after": "always-single-line", 14 | "block-opening-brace-space-before": null, 15 | "color-hex-case": null, 16 | "color-hex-length": null, 17 | "color-named": null, 18 | "comment-empty-line-before": null, 19 | "comment-no-empty": null, 20 | "comment-whitespace-inside": null, 21 | "custom-property-empty-line-before": null, 22 | "declaration-bang-space-after": "never", 23 | "declaration-bang-space-before": null, 24 | "declaration-block-no-duplicate-properties": null, 25 | "declaration-block-semicolon-newline-after": "always-multi-line", 26 | "declaration-block-semicolon-space-after": "always-single-line", 27 | "declaration-block-semicolon-space-before": "never", 28 | "declaration-block-single-line-max-declarations": null, 29 | "declaration-block-trailing-semicolon": "always", 30 | "declaration-colon-newline-after": null, 31 | "declaration-colon-space-after": "always-single-line", 32 | "declaration-colon-space-before": "never", 33 | "declaration-empty-line-before": ["always", { 34 | "except": ["after-declaration", "first-nested"], 35 | "ignore": ["after-comment", "inside-single-line-block"] 36 | }], 37 | "font-family-name-quotes": "always-where-recommended", 38 | "font-family-no-duplicate-names": true, 39 | "function-comma-newline-after": "always-multi-line", 40 | "function-comma-space-after": "always-single-line", 41 | "function-comma-space-before": "never", 42 | "function-max-empty-lines": 0, 43 | "function-name-case": "lower", 44 | "function-parentheses-newline-inside": "always-multi-line", 45 | "function-parentheses-space-inside": "never-single-line", 46 | "function-url-quotes": "always", 47 | "function-whitespace-after": "always", 48 | "indentation": null, 49 | "length-zero-no-unit": true, 50 | "max-empty-lines": 1, 51 | "media-feature-colon-space-after": "always", 52 | "media-feature-colon-space-before": "never", 53 | "media-feature-name-case": "lower", 54 | "media-feature-parentheses-space-inside": "never", 55 | "media-feature-range-operator-space-after": "always", 56 | "media-feature-range-operator-space-before": "always", 57 | "media-query-list-comma-newline-after": "always-multi-line", 58 | "media-query-list-comma-space-after": "always-single-line", 59 | "media-query-list-comma-space-before": "never", 60 | "no-eol-whitespace": true, 61 | "no-missing-end-of-source-newline": true, 62 | "number-leading-zero": "never", 63 | "number-max-precision": 3, 64 | "number-no-trailing-zeros": true, 65 | "property-case": "lower", 66 | "rule-empty-line-before": null, 67 | "selector-attribute-brackets-space-inside": "never", 68 | "selector-attribute-operator-space-after": "never", 69 | "selector-attribute-operator-space-before": "never", 70 | "selector-combinator-space-after": null, 71 | "selector-combinator-space-before": null, 72 | "selector-descendant-combinator-no-non-space": true, 73 | "selector-list-comma-newline-after": null, 74 | "selector-list-comma-space-before": "never", 75 | "selector-max-empty-lines": 0, 76 | "selector-pseudo-class-case": "lower", 77 | "selector-pseudo-class-parentheses-space-inside": "never", 78 | "selector-pseudo-element-case": "lower", 79 | "selector-pseudo-element-colon-notation": "single", 80 | "selector-type-case": "lower", 81 | "selector-type-no-unknown": null, 82 | "string-quotes": "double", 83 | "unit-case": "lower", 84 | "value-list-comma-newline-after": null, 85 | "value-list-comma-space-after": "always-single-line", 86 | "value-list-comma-space-before": "never", 87 | "value-list-max-empty-lines": 0 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © Rob Garrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 9 | Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **ALERT!** 2 | 3 | Medium has completely changed their CSS framework, so this extension and the userstyle won't work any more. 4 | 5 | The previous framework used desriptive class names like "u-backgroundBlueGrey"; but now everything is one to two letters long, e.g. "a b c" 6 | 7 | ![](https://user-images.githubusercontent.com/136959/63128307-2fc70c00-bf7a-11e9-9838-c4ae605e0520.png) 8 | 9 | I don't have the time, nor the patience to decrypt that mess. :crying_cat_face: 10 | 11 | ------- 12 | 13 | # Darker Medium [![tags][tag-img]][tag-url] [![devDependency Status][david-dev-img]][david-dev-url] 14 | 15 | [tag-url]: https://github.com/Mottie/Darker-Medium/tags 16 | [tag-img]: https://img.shields.io/github/tag/Mottie/Darker-Medium.svg 17 | [david-dev-url]: https://david-dm.org/Mottie/Darker-Medium#info=devDependencies 18 | [david-dev-img]: https://david-dm.org/Mottie/Darker-Medium/dev-status.svg 19 | 20 | Read Medium stories with happy eyes! 21 | 22 | ![Screenshot](images/Medium-site-screenshot.png) 23 | 24 | ## Contents 25 | 26 | * [Features](#features) 27 | * [Security Concerns](#security-concerns) 28 | * [Branding](#branding) 29 | * [Installation](#installation) 30 | * [Extension](#extension) 31 | * [Usercss](#usercss) 32 | * [Usage](#usage) 33 | * [Toggle the style (temporarily)](#toggle-the-style-temporarily) 34 | * [Customize accent colors](#customize-accent-colors) 35 | * [Contributing](#contributing) 36 | * [Reporting problems](#reporting-problems) 37 | * [Style issues](#style-issues) 38 | * [Other issues](#other-issues) 39 | 40 | ## Features 41 | 42 | * Applies a customizable dark theme to all medium formatted stories. 43 | * Customize: 44 | * Header background. 45 | * Main Accent color. 46 | * Highlighting color. 47 | * Link color, hover and underline. 48 | * Hiding the "get updates" footer. 49 | * Adds syntax highlighting to code and styles embedded [gists](https://gist.github.com/). 50 | * Medium story sites are *automatically* detected and styled. 51 | * The advantage of using this extension over [Stylus](http://add0n.com/stylus.html) or Stylish is that you install it, customize it and forget about it. 52 | * Please see [this wiki page](https://github.com/Mottie/Darker-Medium/wiki/Tested-pages) for a list of sites that were tested for this extension. 53 | * A user.css file is available, and once Stylus has implemented [support for user.css files](https://github.com/openstyles/stylus/pull/134), it will be immediately usable and installable from this repository. 54 | 55 | ## Security Concerns 56 | 57 | This browser extension **does not collect any personal data**. 58 | 59 | The only data stored is the user style preferences (the state of the extension, five colors & an underline setting). Absolutely nothing else is shared or tracked; not even Google Analytics. 60 | 61 | ## Branding 62 | 63 | Apologizes to the sites that use specific colors in their branding, this extension overrides all of your colors; and it definitely doesn't touch your logo! 64 | 65 | For example, I love hackernoon.com, but your bright green header and banners make my eyes scream when I try to read your site at night. This web extension applies an overall dark theme to all sites including those bright headers. 66 | 67 | ## Installation 68 | 69 | ### Extension 70 | 71 | Install it for: 72 | * [Chrome](https://chrome.google.com/webstore/detail/darker-medium/ffhffkeoooocikeclopcedglnjhoddfj) 73 | * [Firefox](https://addons.mozilla.org/en-US/firefox/addon/darker-medium/) 74 | * [Opera](https://addons.opera.com/en/extensions/details/darker-medium/) 75 | * Safari (Help wanted; See [issue #1](https://github.com/Mottie/Darker-Medium/issues/1)) 76 | 77 | ### Usercss 78 | 79 | [Stylus](http://add0n.com/stylus.html) supports user.css file formats. When a "raw" version of `darker-medium.user.css` from this repository (link below) is opened in the browser, Stylus will prompt you to install the style. For more details, see the [usercss documentation](https://github.com/openstyles/stylus/wiki/Usercss). 80 | 81 | [![Install the main usercss](https://img.shields.io/badge/Install%20main%20usercss%20with-Stylus-285959.svg)](https://raw.githubusercontent.com/Mottie/Darker-Medium/master/darker-medium.user.css) 82 | 83 | [![Install the extras usercss](https://img.shields.io/badge/Install%20extras%20usercss%20with-Stylus-285959.svg)](https://raw.githubusercontent.com/Mottie/Darker-Medium/master/darker-medium-extras.user.css) (Medium.com profile & topic page styling) 84 | 85 | The **advantages** of using a usercss are as follows: 86 | 87 | * No intermediary domain is needed to host the usercss file. 88 | * Updates will be immediately available. 89 | * Style customization is built-in. The same customizations for the extension are also available. 90 | 91 | The **disadvantage** of this specific usercss is: 92 | 93 | * Syntax highlighting is not applied to code snippets, because it requires JavaScript processing. 94 | * Medium stories on unlisted domains will not be styled. If you want to report an unstyled Medium domain, please look in the [other issues](#other-issues) section for more details. A list of currently supported sites can be found in the [`medium-sites.js` file](./build/medium-sites.js). 95 | 96 | **NOTE**: The version number of the available web extension may lag behind the usercss because the usercss style needs updating to add more sites, while the web extension will detect them automatically. 97 | 98 | ## Usage 99 | 100 | ### Toggle the style (temporarily) 101 | 102 | Click on the Darker Medium icon to toggle the dark style on and off *temporarily* for the visible page. This state is not stored; this may be added in a future update. 103 | 104 | ![Toggle Darker](images/Darker-Medium-toggle.gif) 105 | 106 | ### Customize accent colors 107 | 108 | Open the Extension options (all settings are stored): 109 | 110 | * `Enabled` - This is a global toggle of the Darker Medium extension. 111 | * `Header` - Customize the background color of the page header. 112 | * `Main` - Customize the main accent color. This is overrides the branded color of the site. 113 | * `Highlight` - Customize the highlighted text background color. The text color is automatically adjusted as needed. 114 | * `Link Color` - Customize all link colors. This may include icons as well as links within the article. 115 | * `Link Hover` - Customize the link hover color. 116 | * `Include link underline` - Include or hide link underlines. 117 | * `Hide Footer` - Show or Hide the signup/get updates footer. 118 | * `Reset` - Restore the default settings. 119 | 120 | ![Options](images/Darker-Medium-options.png) 121 | 122 | ## Contributing 123 | 124 | If you would like to contribute to this repository, please refer to the [contribution guidelines](./.github/CONTRIBUTING.md). 125 | 126 | And don't worry if you've never contributed to an open-source project before, you can start by reading [this friendly guide](https://github.com/Roshanjossey/first-contributions#readme). 127 | 128 | **This project is bound by a [Code of Conduct](./.github/CODE_OF_CONDUCT.md)**. 129 | 130 | ## Reporting problems 131 | 132 | [![gitter-image]][gitter-url] 133 | 134 | ### Style issues 135 | 136 | * The profile & topic pages of Medium.com _do not use_ the same css framework as the main stories pages. To make these pages 137 | dark, install the `darker-medium-extras.user.css` usercss style linked above (Stylus is needed). 138 | * If it's a public page, you'll only need to report the URL and any steps needed to show the problematic area. 139 | * If you need a membership to access the page, then please include the HTML and related CSS: 140 | * First, you'll need to right click on top of the element, and select "Inspect" or "Inspect Element". 141 | * The Developer Tools panel will open. 142 | * Make sure the highlighted element and the element above it are visible in the panel. 143 | * Look to the right side of that panel and you should find the "Styles" (or "Rules") tab. 144 | * Now look for the problematic style (usually a `background-color` or `color`). 145 | * Share the HTML/CSS by either: 146 | * Copy/pasting the HTML and associated CSS into a new issue, OR 147 | * Take a screenshot of the development panel (if you need an app, try http://getgreenshot.org/downloads/). 148 | * If these instructions aren't clear enough, or you want to contact a person, use Gitter (click the button above) for this repository. 149 | 150 | [gitter-url]: https://gitter.im/Darker-Medium 151 | [gitter-image]: https://img.shields.io/badge/GITTER-join%20chat-yellowgreen.svg 152 | 153 | ### Other issues 154 | 155 | * Please report: 156 | * If public, and pertinent, please share the URL of the page with the problem. 157 | * Report any javascript error as seen in the development tools console (Press F12 to open it). 158 | * Any extra information as described in the issue template when a new issue is opened. 159 | * Do you know how to solve it? It'd be awesome if you [contributed a fix](./.github/CONTRIBUTING.md)! 160 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | "use strict"; 3 | 4 | const pkg = require("../package.json"), 5 | manifest = require("../manifest.json"), 6 | 7 | { 8 | del, 9 | updateSettings, 10 | copyPrettify, 11 | buildUserCSS, 12 | writeFile, 13 | convertToString, 14 | createZip 15 | } = require("./files"); 16 | 17 | manifest.version = pkg.version; 18 | 19 | del("darker-medium.zip") 20 | .then(() => del("darker-medium.xpi")) 21 | .then(() => del("darker-medium.user.css")) 22 | .then(() => updateSettings("js/settings.js", 2)) 23 | .then(() => updateSettings("js/content.js", 4)) 24 | .then(() => copyPrettify()) 25 | .then(() => buildUserCSS(pkg.version)) 26 | .then(() => writeFile("manifest.json", convertToString(manifest))) 27 | .then(() => createZip("darker-medium.zip", manifest)) 28 | .then(() => { 29 | // Psuedo sign XPI for local testing 30 | manifest.applications = { 31 | gecko: { 32 | id: "darker-medium@example.com" 33 | } 34 | }; 35 | return createZip("darker-medium.xpi", manifest); 36 | }) 37 | .then(() => console.log("\x1b[32m%s\x1b[0m", "Darker-Medium build complete")) 38 | .catch(error => { 39 | throw error; 40 | }); 41 | -------------------------------------------------------------------------------- /build/files.js: -------------------------------------------------------------------------------- 1 | /* global require module */ 2 | "use strict"; 3 | 4 | const fs = require("fs"), 5 | archiver = require("archiver"), 6 | processTemplate = require("./usercss"), 7 | 8 | settings = require("./settings.json"), 9 | 10 | regex = new RegExp(` 11 | \\/\\*\\s*BUILD:SETTINGS_START\\s*\\*\\/ 12 | [\\s\\S]+ 13 | \\/\\*\\s*BUILD:SETTINGS_END\\s*\\*\\/ 14 | `.replace(/\s/g, ""), 15 | "g" // eslint-disable-line indent 16 | ); 17 | 18 | function readFile(name) { 19 | return new Promise((resolve, reject) => { 20 | fs.readFile(name, "utf8", (err, file) => { 21 | if (err) { 22 | return reject(err); 23 | } 24 | resolve(file); 25 | }); 26 | }); 27 | } 28 | 29 | function writeFile(name, obj) { 30 | return new Promise((resolve, reject) => { 31 | fs.writeFile(name, obj, "utf8", err => { 32 | if (err) { 33 | console.log(`Error writing ${name}`, err); 34 | return reject(); 35 | } 36 | resolve(); 37 | }); 38 | }); 39 | } 40 | 41 | function del(name) { 42 | return new Promise((resolve, reject) => { 43 | fs.unlink(name, err => { 44 | // ignore if file doesn't exist 45 | if (err && err.code !== "ENOENT") { 46 | return reject(); 47 | } 48 | resolve(); 49 | }); 50 | }); 51 | } 52 | 53 | function convertToString(obj, tabs = "\t") { 54 | return JSON.stringify(obj, null, tabs) + "\n"; 55 | } 56 | 57 | // Replace defaults in content.js and settings.js 58 | function replaceSettings(str, tabs) { 59 | const tabStr = "\t".repeat(tabs), 60 | result = convertToString(settings, tabStr); 61 | return str.replace( 62 | regex, 63 | "/* BUILD:SETTINGS_START */" + 64 | // Strip off curly brackets & "\n" at end 65 | `${result.substring(1, result.length - 2)}${tabStr}` + 66 | "/* BUILD:SETTINGS_END */" 67 | ); 68 | } 69 | 70 | function updateSettings(name, tabs) { 71 | return readFile(name) 72 | .then(file => replaceSettings(file, tabs)) 73 | .then(file => writeFile(name, file)); 74 | } 75 | 76 | function copyPrettify() { 77 | readFile("node_modules/code-prettify/src/run_prettify.js") 78 | .then(code => { 79 | writeFile("./js/run_prettify.js", code); 80 | }); 81 | } 82 | 83 | // Build darker-medium.user.css from template and style.css 84 | function buildUserCSS(version) { 85 | const files = [ 86 | "./build/template.user.css", // Must be first in this list 87 | "./style-profiles.css", 88 | "./style.css", 89 | "./gist.css" 90 | ]; 91 | return Promise.all(files.map(name => readFile(name))) 92 | .then(files => writeFile( 93 | "darker-medium.user.css", processTemplate(files, version)) 94 | ); 95 | } 96 | 97 | function createZip(name, obj) { 98 | const file = fs.createWriteStream(name), 99 | archive = archiver("zip"), 100 | json = convertToString(obj); 101 | return new Promise((resolve, reject) => { 102 | archive.on("finish", () => { 103 | resolve(); 104 | }); 105 | archive.on("warning", err => { 106 | if (err.code === "ENOENT") { 107 | console.log("\x1b[33m%s\x1b[0m", "Warning", err.message); 108 | } else { 109 | throw err; 110 | } 111 | }); 112 | archive.on("error", err => { 113 | reject(); 114 | throw err; 115 | }); 116 | 117 | archive.pipe(file); 118 | archive.glob("icons/*"); 119 | archive.glob("js/*"); 120 | archive.glob("options/*"); 121 | archive.glob("style.css"); 122 | archive.glob("gist.css"); 123 | archive.append(json, { 124 | name: "manifest.json" 125 | }); 126 | archive.finalize(); 127 | }); 128 | } 129 | 130 | module.exports = { 131 | del, 132 | updateSettings, 133 | copyPrettify, 134 | buildUserCSS, 135 | writeFile, 136 | convertToString, 137 | createZip 138 | }; 139 | -------------------------------------------------------------------------------- /build/medium-sites.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | "use strict"; 3 | 4 | /* Update this list of domains for Medium story sites 5 | * See this page for valid document rules: 6 | * https://github.com/stylish-userstyles/stylish/wiki/Valid-@-moz-document-rules 7 | * Examples: 8 | * - domain("example.com") => ["domain", "example.com"] 9 | * - url(http://www.example.com/page.html) => ["url", "http://www.example.com/page.html"] 10 | * - url-prefix(http://www.example.) => ["url-prefix", "http://www.example."] 11 | * - regexp("http://(www|blog)\\.example\\.com/.*") => ["regexp", "http://(www|blog)\\.example\\.com/.*"] 12 | */ 13 | module.exports = [ 14 | ["domain", "500ish.com"], 15 | ["domain", "android.jlelse.eu"], 16 | ["domain", "arcdigital.media"], 17 | ["domain", "artistwaves.com"], 18 | ["domain", "artplusmarketing.com"], 19 | ["domain", "bettereveryday.vc"], 20 | ["domain", "betterhumans.coach.me"], 21 | ["domain", "blog.angularindepth.com"], 22 | ["domain", "blog.bitsrc.io"], 23 | ["domain", "blog.codacy.com"], 24 | ["domain", "blog.cloudboost.io"], 25 | ["domain", "blog.creandum.com"], 26 | ["domain", "blog.doist.com"], 27 | ["domain", "blog.figma.com"], 28 | ["domain", "blog.fontawesome.com"], 29 | ["domain", "blog.framer.com"], 30 | ["domain", "blog.gofundme.com"], 31 | ["domain", "blog.gojekengineering.com"], 32 | ["domain", "blog.goodaudience.com"], 33 | ["domain", "blog.graphql.guide"], 34 | ["domain", "blog.hackster.io"], 35 | ["domain", "blog.heartsupport.com"], 36 | ["domain", "blog.inkdrop.info"], 37 | ["domain", "blog.kentcdodds.com"], 38 | ["domain", "blog.leanstack.com"], 39 | ["domain", "blog.lftechnology.com"], 40 | ["domain", "blog.logrocket.com"], 41 | ["domain", "blog.markgrowth.com"], 42 | ["domain", "blog.mode7games.com"], 43 | ["domain", "blog.neufund.org"], 44 | ["domain", "blog.pacific-content.com"], 45 | ["domain", "blog.producthunt.com"], 46 | ["domain", "blog.prototypr.io"], 47 | ["domain", "blog.realkinetic.com"], 48 | ["domain", "blog.sindresorhus.com"], 49 | ["domain", "blog.sketchapp.com"], 50 | ["domain", "blog.sourcerer.io"], 51 | ["domain", "blog.thunkable.com"], 52 | ["domain", "blog.timescale.com"], 53 | ["domain", "blog.usejournal.com"], 54 | ["domain", "blog.zenkit.com"], 55 | ["domain", "bothsidesofthetable.com"], 56 | ["domain", "brightthemag.com"], 57 | ["domain", "builttoadapt.io"], 58 | ["domain", "byrslf.co"], 59 | ["domain", "charting-ahead.corsairs.network"], 60 | ["domain", "chunksofco.de"], 61 | ["domain", "civicskunk.works"], 62 | ["domain", "codeburst.io"], 63 | ["domain", "cosgrrrl.com"], 64 | ["domain", "creative-analytics.corsairs.network"], 65 | ["domain", "crossingenres.com"], 66 | ["domain", "dearoldpeople.com"], 67 | ["domain", "digitalculturist.com"], 68 | ["domain", "eand.co"], 69 | ["domain", "edgecoders.com"], 70 | ["domain", "eidolon.pub"], 71 | ["domain", "electricliterature.com"], 72 | ["domain", "engineering.hexacta.com"], 73 | ["domain", "entrepreneurs.maqtoob.com"], 74 | ["domain", "entrepreneurshandbook.co"], 75 | ["domain", "extranewsfeed.com"], 76 | ["domain", "fellowsblog.ted.com"], 77 | ["domain", "fromupnorth.com"], 78 | ["domain", "ginosblog.com"], 79 | ["domain", "hackernoon.com"], 80 | ["domain", "howwegettonext.com"], 81 | ["domain", "injusticetoday.com"], 82 | ["domain", "journal.thriveglobal.com"], 83 | ["domain", "learn.fontself.com"], 84 | ["domain", "levelup.gitconnected.com"], 85 | ["domain", "loremipsum.ueno.co"], 86 | ["domain", "m.signalvnoise.com"], 87 | ["domain", "media.consensys.net"], 88 | ["domain", "medium.com"], 89 | ["domain", "melmagazine.com"], 90 | ["domain", "midcenturymodernmag.com"], 91 | ["domain", "mondaynote.com"], 92 | ["domain", "movietime.guru"], 93 | ["domain", "mystudentvoices.com"], 94 | ["domain", "netbasal.com"], 95 | ["domain", "news.greylock.com"], 96 | ["domain", "nexusmedianews.com"], 97 | ["domain", "ofdollarsanddata.com"], 98 | ["domain", "omgfacts.com"], 99 | ["domain", "orangeandwhitereport.com"], 100 | ["domain", "pillow.codes"], 101 | ["domain", "planamag.com"], 102 | ["domain", "points.datasociety.net"], 103 | ["domain", "ppcdept.com"], 104 | ["domain", "praxis.fortelabs.co"], 105 | ["domain", "proandroiddev.com"], 106 | ["domain", "psiloveyou.xyz"], 107 | ["domain", "quipblog.com"], 108 | ["domain", "rantt.com"], 109 | ["domain", "read.compassofdesign.com"], 110 | ["domain", "ryanluikens.com"], 111 | ["domain", "shift.newco.co"], 112 | ["domain", "slack.design"], 113 | ["domain", "slack.engineering"], 114 | ["domain", "slackhq.com"], 115 | ["domain", "sprintstories.com"], 116 | ["domain", "spuddings.net"], 117 | ["domain", "startupsventurecapital.com"], 118 | ["domain", "tech.kartenmacherei.de"], 119 | ["domain", "tech.residebrokerage.com"], 120 | ["domain", "thebelladonnacomedy.com"], 121 | ["domain", "thebolditalic.com"], 122 | ["domain", "thecoffeelicious.com"], 123 | ["domain", "thedesignteam.io"], 124 | ["domain", "theestablishment.co"], 125 | ["domain", "thegospeleconomist.com"], 126 | ["domain", "thelily.com"], 127 | ["domain", "thesternfacts.com"], 128 | ["domain", "thestyleofelements.org"], 129 | ["domain", "thinkgrowth.org"], 130 | ["domain", "thomasdespin.com"], 131 | ["domain", "timeline.com"], 132 | ["domain", "towardsdatascience.com"], 133 | ["domain", "trackchanges.postlight.com"], 134 | ["domain", "umairhaque.com"], 135 | ["domain", "ux.useronboard.com"], 136 | ["domain", "uxdesign.cc"], 137 | ["domain", "uxplanet.org"], 138 | ["domain", "witness.worldpressphoto.org"], 139 | ["domain", "words.werd.io"], 140 | ["domain", "writingcooperative.com"], 141 | /* A very general regular expression looking for a "medium" subdomain 142 | * so far, it captures: 143 | * - https://medium.muz.li 144 | * - https://medium.freecodecamp.org 145 | */ 146 | ["regexp", "https?://[^\\/]*medium\\.\\S*"] 147 | ]; 148 | -------------------------------------------------------------------------------- /build/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "#1d1d1d", 3 | "main": "#7AA8D6", 4 | "highlight": "#7AA8D6", 5 | "link": "#dddddd", 6 | "hover": "#dddddd", 7 | "underline": 1, 8 | "footer": 0 9 | } 10 | -------------------------------------------------------------------------------- /build/template.user.css: -------------------------------------------------------------------------------- 1 | /* ==UserStyle== 2 | @name Darker Medium 3 | @version / * BUILD:VERSION * / 4 | @description Read Medium stories with happy eyes 5 | @namespace https://github.com/Mottie 6 | @author Rob Garrison 7 | @homepageURL https://github.com/Mottie/Darker-Medium 8 | @supportURL https://github.com/Mottie/Darker-Medium/issues 9 | @updateURL https://mottie.github.io/Darker-Medium/darker-medium.user.css 10 | @license CC-BY-SA-4.0 11 | @preprocessor stylus 12 | @var color acc_header "Header background" / * BUILD:HEADER * / 13 | @var color acc_main "Main Accent Color" / * BUILD:MAIN * / 14 | @var color acc_highlight "Highlight Color" / * BUILD:HIGHLIGHT * / 15 | @var color acc_link "Link Color" / * BUILD:LINK * / 16 | @var color acc_hover "Link Hover Color" / * BUILD:HOVER * / 17 | @var checkbox acc_underline "Link underline" / * BUILD:UNDERLINE * / 18 | @var checkbox hide_footer "Hide Footer" / * BUILD:FOOTER * / 19 | ==/UserStyle== */ 20 | /* DO NOT EDIT THIS USERCSS - IT IS GENERATED AUTOMATICALLY */ 21 | /* Update the supported Medium stories domains in "medium-sites.json" */ 22 | /* BUILD:MOZDOC */ 23 | :root { 24 | --accent_header: acc_header; 25 | --accent_main: acc_main; 26 | --accent_highlight: acc_highlight; 27 | --accent_link: acc_link; 28 | --accent_hover: acc_hover; 29 | 30 | --accent_main_text: dark(acc_main) ? white : black; 31 | --accent_highlight_text: dark(acc_highlight) ? white : black; 32 | --accent_underline: alpha(acc_link, 0.68); 33 | --hide_footer: hide_footer ? none : block; 34 | } 35 | 36 | /* BUILD:USER_CSS */ 37 | 38 | /* BUILD:PROFILES */ 39 | -------------------------------------------------------------------------------- /build/usercss.js: -------------------------------------------------------------------------------- 1 | /* global require module */ 2 | "use strict"; 3 | 4 | const sites = require("./medium-sites"), 5 | settings = require("./settings.json"); 6 | 7 | function buildMozDoc() { 8 | const list = sites.map(fxn => `${fxn[0]}("${fxn[1]}")`); 9 | return `@-moz-document ${list.join(",\n")} {\n`; 10 | } 11 | 12 | function replaceVar(str, name, replacement) { 13 | const regex = new RegExp(`\\/\\s*\\*\\s*BUILD:${name}\\s*\\*\\s*\\/`, "gi"); 14 | return str.replace(regex, replacement); 15 | } 16 | 17 | // Parameter files > [ template.user.css, style-profiles.css, style.css, gist.css ] 18 | module.exports = function(files, version) { 19 | // / * BUILD:VERSION * / 20 | // @var color acc_header "Header background" / * BUILD:HEADER * / 21 | // @var color acc_main "Main Accent Color" / * BUILD:MAIN * / 22 | // @var color acc_highlight "Highlight Color" / * BUILD:HIGHLIGHT * / 23 | // @var color acc_link "Link Color" / * BUILD:LINK * / 24 | // @var color acc_hover "Link Hover Color" / * BUILD:HOVER * / 25 | // @var checkbox acc_underline "Link underline" / * BUILD:UNDERLINE * / 26 | // @var checkbox hide_footer "Hide Footer" / * BUILD:FOOTER * / 27 | // /* BUILD:USER_CSS */ 28 | let usercss = files.shift(); 29 | 30 | usercss = replaceVar(usercss, "version", version); 31 | Object.keys(settings).forEach(key => { 32 | usercss = replaceVar(usercss, key, settings[key]); 33 | }); 34 | usercss = replaceVar(usercss, "profiles", files.shift()); 35 | 36 | // Style.css + gist.css wrapped by @-moz-document rule 37 | usercss = replaceVar(usercss, "mozdoc", buildMozDoc()); 38 | // Remove :root{} from gist.css; otherwise, it'll be duplicated in the 39 | // usercss, and is needed in the gist style injected by the extension into 40 | // the iframe 41 | files.forEach((file, index) => { 42 | const indx = file.indexOf("/* Gist Syntax"); 43 | if (indx > -1) { 44 | files[index] = file.substring(indx, file.length); 45 | } 46 | }); 47 | return replaceVar(usercss, "user_css", `${files.join("\n")}\n}`); 48 | }; 49 | -------------------------------------------------------------------------------- /darker-medium-extras.user.css: -------------------------------------------------------------------------------- 1 | /* ==UserStyle== 2 | @name Darker Medium Profiles/Topics 3 | @version 1.0.0 4 | @description Read Medium story profiles & topics with happy eyes 5 | @namespace https://github.com/Mottie 6 | @author Rob Garrison 7 | @homepageURL https://github.com/Mottie/Darker-Medium 8 | @supportURL https://github.com/Mottie/Darker-Medium/issues 9 | @updateURL https://mottie.github.io/Darker-Medium/darker-medium-extras.user.css 10 | @license CC-BY-SA-4.0 11 | ==/UserStyle== */ 12 | @-moz-document url-prefix("https://medium.com/") { 13 | /* UserCSS only (not part of the extension): 14 | profile pages - https://medium.com/@blah 15 | topics pages - https://medium.com/topic/self (etc) 16 | */ 17 | body, .c { 18 | background: var(--color_bkg_11) !important; 19 | color: var(--color_txt_aa) !important; 20 | } 21 | nav.m { 22 | background: var(--accent_header) !important; 23 | } 24 | 25 | a, .gx, .gy { 26 | color: var(--accent_main) !important; 27 | border-color: var(--accent_main) !important; 28 | } 29 | 30 | a svg { 31 | fill: currentColor !important; 32 | filter: none !important; 33 | } 34 | 35 | div.k, .dn { 36 | background: var(--color_bkg_11) !important; 37 | border-color: var(--color_brd_33) !important; 38 | } 39 | 40 | li { 41 | border-color: var(--color_brd_33) !important; 42 | } 43 | 44 | h1, 45 | h2, 46 | h3, 47 | h4, 48 | h5, 49 | h6, 50 | div, 51 | span { 52 | color: var(--color_txt_aa) !important; 53 | } 54 | 55 | svg { 56 | filter: invert(100%) !important; 57 | } 58 | } -------------------------------------------------------------------------------- /darker-medium.user.css: -------------------------------------------------------------------------------- 1 | /* ==UserStyle== 2 | @name Darker Medium 3 | @version 1.4.3 4 | @description Read Medium stories with happy eyes 5 | @namespace https://github.com/Mottie 6 | @author Rob Garrison 7 | @homepageURL https://github.com/Mottie/Darker-Medium 8 | @supportURL https://github.com/Mottie/Darker-Medium/issues 9 | @updateURL https://mottie.github.io/Darker-Medium/darker-medium.user.css 10 | @license CC-BY-SA-4.0 11 | @preprocessor stylus 12 | @var color acc_header "Header background" #1d1d1d 13 | @var color acc_main "Main Accent Color" #7AA8D6 14 | @var color acc_highlight "Highlight Color" #7AA8D6 15 | @var color acc_link "Link Color" #dddddd 16 | @var color acc_hover "Link Hover Color" #dddddd 17 | @var checkbox acc_underline "Link underline" 1 18 | @var checkbox hide_footer "Hide Footer" 0 19 | ==/UserStyle== */ 20 | /* DO NOT EDIT THIS USERCSS - IT IS GENERATED AUTOMATICALLY */ 21 | /* Update the supported Medium stories domains in "medium-sites.json" */ 22 | @-moz-document domain("500ish.com"), 23 | domain("android.jlelse.eu"), 24 | domain("arcdigital.media"), 25 | domain("artistwaves.com"), 26 | domain("artplusmarketing.com"), 27 | domain("bettereveryday.vc"), 28 | domain("betterhumans.coach.me"), 29 | domain("blog.angularindepth.com"), 30 | domain("blog.bitsrc.io"), 31 | domain("blog.codacy.com"), 32 | domain("blog.cloudboost.io"), 33 | domain("blog.creandum.com"), 34 | domain("blog.doist.com"), 35 | domain("blog.figma.com"), 36 | domain("blog.fontawesome.com"), 37 | domain("blog.framer.com"), 38 | domain("blog.gofundme.com"), 39 | domain("blog.gojekengineering.com"), 40 | domain("blog.goodaudience.com"), 41 | domain("blog.graphql.guide"), 42 | domain("blog.hackster.io"), 43 | domain("blog.heartsupport.com"), 44 | domain("blog.inkdrop.info"), 45 | domain("blog.kentcdodds.com"), 46 | domain("blog.leanstack.com"), 47 | domain("blog.lftechnology.com"), 48 | domain("blog.logrocket.com"), 49 | domain("blog.markgrowth.com"), 50 | domain("blog.mode7games.com"), 51 | domain("blog.neufund.org"), 52 | domain("blog.pacific-content.com"), 53 | domain("blog.producthunt.com"), 54 | domain("blog.prototypr.io"), 55 | domain("blog.realkinetic.com"), 56 | domain("blog.sindresorhus.com"), 57 | domain("blog.sketchapp.com"), 58 | domain("blog.sourcerer.io"), 59 | domain("blog.thunkable.com"), 60 | domain("blog.timescale.com"), 61 | domain("blog.usejournal.com"), 62 | domain("blog.zenkit.com"), 63 | domain("bothsidesofthetable.com"), 64 | domain("brightthemag.com"), 65 | domain("builttoadapt.io"), 66 | domain("byrslf.co"), 67 | domain("charting-ahead.corsairs.network"), 68 | domain("chunksofco.de"), 69 | domain("civicskunk.works"), 70 | domain("codeburst.io"), 71 | domain("cosgrrrl.com"), 72 | domain("creative-analytics.corsairs.network"), 73 | domain("crossingenres.com"), 74 | domain("dearoldpeople.com"), 75 | domain("digitalculturist.com"), 76 | domain("eand.co"), 77 | domain("edgecoders.com"), 78 | domain("eidolon.pub"), 79 | domain("electricliterature.com"), 80 | domain("engineering.hexacta.com"), 81 | domain("entrepreneurs.maqtoob.com"), 82 | domain("entrepreneurshandbook.co"), 83 | domain("extranewsfeed.com"), 84 | domain("fellowsblog.ted.com"), 85 | domain("fromupnorth.com"), 86 | domain("ginosblog.com"), 87 | domain("hackernoon.com"), 88 | domain("howwegettonext.com"), 89 | domain("injusticetoday.com"), 90 | domain("journal.thriveglobal.com"), 91 | domain("learn.fontself.com"), 92 | domain("levelup.gitconnected.com"), 93 | domain("loremipsum.ueno.co"), 94 | domain("m.signalvnoise.com"), 95 | domain("media.consensys.net"), 96 | domain("medium.com"), 97 | domain("melmagazine.com"), 98 | domain("midcenturymodernmag.com"), 99 | domain("mondaynote.com"), 100 | domain("movietime.guru"), 101 | domain("mystudentvoices.com"), 102 | domain("netbasal.com"), 103 | domain("news.greylock.com"), 104 | domain("nexusmedianews.com"), 105 | domain("ofdollarsanddata.com"), 106 | domain("omgfacts.com"), 107 | domain("orangeandwhitereport.com"), 108 | domain("pillow.codes"), 109 | domain("planamag.com"), 110 | domain("points.datasociety.net"), 111 | domain("ppcdept.com"), 112 | domain("praxis.fortelabs.co"), 113 | domain("proandroiddev.com"), 114 | domain("psiloveyou.xyz"), 115 | domain("quipblog.com"), 116 | domain("rantt.com"), 117 | domain("read.compassofdesign.com"), 118 | domain("ryanluikens.com"), 119 | domain("shift.newco.co"), 120 | domain("slack.design"), 121 | domain("slack.engineering"), 122 | domain("slackhq.com"), 123 | domain("sprintstories.com"), 124 | domain("spuddings.net"), 125 | domain("startupsventurecapital.com"), 126 | domain("tech.kartenmacherei.de"), 127 | domain("tech.residebrokerage.com"), 128 | domain("thebelladonnacomedy.com"), 129 | domain("thebolditalic.com"), 130 | domain("thecoffeelicious.com"), 131 | domain("thedesignteam.io"), 132 | domain("theestablishment.co"), 133 | domain("thegospeleconomist.com"), 134 | domain("thelily.com"), 135 | domain("thesternfacts.com"), 136 | domain("thestyleofelements.org"), 137 | domain("thinkgrowth.org"), 138 | domain("thomasdespin.com"), 139 | domain("timeline.com"), 140 | domain("towardsdatascience.com"), 141 | domain("trackchanges.postlight.com"), 142 | domain("umairhaque.com"), 143 | domain("ux.useronboard.com"), 144 | domain("uxdesign.cc"), 145 | domain("uxplanet.org"), 146 | domain("witness.worldpressphoto.org"), 147 | domain("words.werd.io"), 148 | domain("writingcooperative.com"), 149 | regexp("https?://[^\/]*medium\.\S*") { 150 | 151 | :root { 152 | --accent_header: acc_header; 153 | --accent_main: acc_main; 154 | --accent_highlight: acc_highlight; 155 | --accent_link: acc_link; 156 | --accent_hover: acc_hover; 157 | 158 | --accent_main_text: dark(acc_main) ? white : black; 159 | --accent_highlight_text: dark(acc_highlight) ? white : black; 160 | --accent_underline: alpha(acc_link, 0.68); 161 | --hide_footer: hide_footer ? none : block; 162 | } 163 | 164 | :root { 165 | --color_bkg_0d: #0d0d0d; 166 | --color_bkg_11: #111; 167 | --color_bkg_1b: #1b1b1b; 168 | --color_bkg_ff: #fff; 169 | 170 | --color_brd_33: #333; 171 | 172 | --color_txt_88: #888; 173 | --color_txt_aa: #aaa; 174 | --color_txt_cc: #ccc; 175 | --color_txt_dd: #ddd; 176 | --color_txt_ee: #eee; 177 | } 178 | 179 | .js-stickyFooter, .postMeterBar { 180 | display: var(--hide_footer) !important; 181 | } 182 | 183 | .metabar, 184 | .postActions, 185 | .cardChromeless, 186 | .canvas-renderer, 187 | .zoomable:before, 188 | /* sticky footer*/ 189 | .u-backgroundWhite.u-bottom0, 190 | .overlay--dark .overlay-dialog, 191 | .overlay--dark .overlay-title--context, 192 | .catalogPreview--card .catalogPreview-header, 193 | .responsesStream-showOtherResponses.cardChromeless { 194 | background-color: var(--color_bkg_0d) !important; 195 | color: var(--color_txt_88) !important; 196 | } 197 | 198 | .drawer, 199 | .drawer-content, 200 | .quoteResponses, 201 | .quoteResponses-section--tips, 202 | .quoteResponses-section--tips:last-child, 203 | .js-carouselInner section .u-borderBox { 204 | background-color: var(--color_bkg_0d) !important; 205 | color: var(--color_txt_aa) !important; 206 | } 207 | 208 | input { 209 | background-color: var(--color_bkg_0d) !important; 210 | color: var(--color_txt_dd) !important; 211 | } 212 | 213 | .tags li, 214 | .tags a:hover, 215 | .tags--borderless > a, 216 | .tags--borderless > li, 217 | .tags--borderless > .button, 218 | .tags--borderless .tag-button, 219 | .u-backgroundColorWhite { 220 | background-color: var(--color_bkg_0d) !important; 221 | border: 0 !important; 222 | } 223 | 224 | pre, 225 | code, 226 | .overlay, 227 | .tags--postTags > a, 228 | .tags--postTags > li, 229 | .tags--postTags > .button, 230 | .tags--postTags .tag-button { 231 | background-color: var(--color_bkg_0d) !important; 232 | } 233 | 234 | html, 235 | body, 236 | article, 237 | .footer, 238 | .site-main, 239 | .radioInput, 240 | .EmbeddedTweet, 241 | .screenContent, 242 | .u-backgroundWhite, 243 | .drawer-keyboardKey, 244 | .overlay-dialog--email, 245 | .jobs-teams__item:after, 246 | .overlay-dialog--signin, 247 | .metabar + .u-tintBgColor, 248 | .u-backgroundBlueGrey, 249 | .u-backgroundGrayLight, 250 | .u-backgroundGrayLighter, 251 | .u-backgroundGrayLightest, 252 | .u-sm-background--brandSea, 253 | .u-backgroundColorGrayLight, 254 | .clapButton--largePill.is-active { 255 | background-color: var(--color_bkg_11) !important; 256 | } 257 | 258 | .js-trackedPromo, 259 | .heading-supplemental, 260 | .upgradePage-valuePropHeader, 261 | .u-background--brandDust, 262 | .u-background--brandPlum, 263 | .u-background--brandRose, 264 | .u-background--brandSage, 265 | .u-background--brandSageLighter, 266 | .u-background--brandSand, 267 | .u-background--brandSea, 268 | .u-background--brandSky, 269 | .u-background--brandCanary, 270 | .streamItemConversationItem--summary, 271 | .TwitterCard-container--clickable:hover { 272 | background-color: var(--color_bkg_1b) !important; 273 | } 274 | 275 | .popover-arrow:after, 276 | .popover .popover-inner, 277 | .popover--dark > .popover-inner, 278 | .popover--dark > .popover-arrow:after, 279 | .u-backgroundTransparentBlackDark, 280 | .u-backgroundTransparentBlackDarker, 281 | .u-backgroundTransparentBlackLight, 282 | .u-backgroundTransparentBlackLighter, 283 | .u-backgroundTransparentBlackLightest, 284 | .u-backgroundColorTransparentBlackLight { 285 | background: var(--color_bkg_1b) !important; 286 | } 287 | 288 | input::-webkit-calendar-picker-indicator:hover { 289 | background: var(--color_bkg_1b) !important; 290 | } 291 | 292 | figure img { 293 | background-color: var(--color_bkg_ff) !important; 294 | opacity: .5 !important; 295 | } 296 | 297 | figure:hover img { 298 | opacity: 1 !important; 299 | } 300 | 301 | .butterBar-message, 302 | .u-backgroundGreenNormal, 303 | .u-backgroundHighlightStrong, 304 | .radioInput.is-active:after, 305 | .radio:checked + .radioInput:after, 306 | body .button.button--filled, 307 | .button.button--filled:focus, 308 | .button.button--filled:hover, 309 | .button.button--filled:active, 310 | .overlay--dark .overlay-title, 311 | .buttonSwitch button[disabled], 312 | .u-accentColor--fillWhenActive.is-active, 313 | .button--primary.button--withChrome.is-active, 314 | .button--primary.button--withChrome.is-active:focus, 315 | .button--primary.button--withChrome.is-active:hover, 316 | .button--primary.button--withChrome.is-active:active, 317 | body .button--withChrome.is-active .button-label.button-activeState, 318 | .js-floatingMultirecommendCount.u-accentColor--backgroundNormal, 319 | .js-stickyFooter .button--dark.button--activity.is-counter, 320 | .js-stickyFooter .button--dark.button--primary.button--withChrome.is-active, 321 | body .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 322 | body .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill) .svgIcon, 323 | body .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill), 324 | body .u-tintSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 325 | body .u-tintSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) { 326 | background-color: var(--accent_main) !important; 327 | color: var(--accent_main_text) !important; 328 | } 329 | 330 | .buttonSwitch .button[disabled] { 331 | box-shadow: 0 0 0 1px var(--accent_main) !important; 332 | } 333 | 334 | body .u-tintSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 335 | body .u-tintSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill):not(.button--follow) { 336 | background-color: var(--accent_link) !important; 337 | color: var(--color_bkg_0d) !important; 338 | } 339 | 340 | .header, 341 | .u-tintBgColor div, 342 | .markup--quote code, 343 | .markup--quote strong, 344 | .u-backgroundTransparentWhiteDarker, 345 | .u-backgroundTransparentWhiteDarkest, 346 | .inputGroup.metabar-predictiveSearch .textInput:not(:focus), 347 | body .u-tintSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill) span { 348 | background-color: transparent !important; 349 | } 350 | 351 | div.popover, 352 | .readNextPromo, 353 | footer .container > div { 354 | background: transparent none !important; 355 | } 356 | 357 | /* fade out content - non-subscription */ 358 | .postFade:before, 359 | .u-backgroundWhite70FadeTop { 360 | background-image: linear-gradient(to bottom, transparent 0, var(--color_bkg_11) 100%) !important; 361 | } 362 | .u-backgroundWhite0FadeTop { 363 | background-image: linear-gradient(0deg, var(--color_bkg_11) 0, hsla(0, 0%, 0%, .5)) !important; 364 | } 365 | 366 | ::-webkit-input-placeholder { 367 | color: var(--color_txt_88) !important; 368 | } 369 | ::-moz-placeholder { 370 | color: var(--color_txt_88) !important; 371 | } 372 | ::placeholder { 373 | color: var(--color_txt_88) !important; 374 | } 375 | 376 | .header, 377 | .button--link, 378 | .u-textColorLight, 379 | .drawer-annotation, 380 | .u-textColorDarkest, 381 | .button--chromeless, 382 | .chartTabs .chartTab, 383 | .heading-supplemental, 384 | .homeContainer-nextUp, 385 | .popover-prePublishTags, 386 | .inputGroup-description, 387 | hr.section-divider:before, 388 | .inlineEditor-placeholder, 389 | .licenseSelector-description, 390 | .listChoice-textColumn--right, 391 | .popover-prePublishSaving, 392 | .popover-prePublishDescription, 393 | .list-item.list-item--inactive { 394 | color: var(--color_txt_88) !important; 395 | } 396 | 397 | p, 398 | body, 399 | time, 400 | article, 401 | section, 402 | figcaption, 403 | .label, 404 | .button, 405 | h3 a.link, 406 | h4 a.link, 407 | .tags .link, 408 | .chartHelper, 409 | .elevate-caps, 410 | .defaultValue, 411 | .popover-inner, 412 | .typeahead-score, 413 | .uiScale .ui-h1, 414 | .uiScale .ui-h2, 415 | .uiScale .ui-h3, 416 | .uiScale .ui-body, 417 | .uiScale .ui-capsSubtle, 418 | .elevate-caps, 419 | .heading-prefix, 420 | .postMetaInline, 421 | .overlay-content, 422 | .lockedPostHeader, 423 | .u-textColorNormal, 424 | .u-textColorDarker, 425 | .u-textColorLighter, 426 | a.link.link--darker, 427 | a.link.link--darken, 428 | .notesMarker .svgIcon, 429 | a.link.u-textColorDarker, 430 | .creditCardForm label, 431 | .quoteItem-attribution, 432 | .heading--privateNotes, 433 | .popover-prepublishTags, 434 | .popover-prepublishDescription, 435 | .markup--mixtapeEmbed-em, 436 | .lockedPostHeader .svgIcon, 437 | .uiScale-ui--large .ui-summary, 438 | .uiScale-ui--small .ui-summary, 439 | .uiScale-ui--regular .ui-summary, 440 | .uiScale-ui--small.ui-heading, 441 | .uiScale-ui--small .ui-heading, 442 | .uiScale-ui--smaller.ui-heading, 443 | .uiScale-ui--smaller .ui-heading, 444 | .uiScale-ui--large .ui-heading, 445 | .uiScale-ui--regular .ui-heading, 446 | .sortableTable-text--visibility, 447 | .overlay--dark .overlay-content, 448 | .popover-inner .popover-description, 449 | .uiScale-caption--small .ui-caption, 450 | .uiScale-caption--regular.ui-caption, 451 | .uiScale-caption--regular .ui-caption, 452 | .postMetaInline-licenseIcons .svgIcon, 453 | .u-tintSpectrum .u-baseColor--textDark, 454 | .u-tintSpectrum .u-baseColor--textNormal, 455 | .u-resetSpectrum .u-baseColor--buttonDark, 456 | .catalogPreview--card .catalogPreview-name, 457 | .u-tintSpectrum .u-baseColor--link.link--darker, 458 | .js-carouselInner section .u-baseColor--textDarker { 459 | color: var(--color_txt_aa) !important; 460 | fill: var(--color_txt_aa) !important; 461 | } 462 | 463 | .u-textColorNormal .circleCountdown-fill { 464 | stroke: var(--color_txt_aa) !important; 465 | } 466 | 467 | .u-fillTransparentBlackDarker { 468 | fill: var(--color_txt_aa) !important; 469 | } 470 | 471 | strong, 472 | .avatar-text, 473 | .heading-title, 474 | .graf--pullquote, 475 | .u-textColorDark, 476 | .u-textColorWhite, 477 | .uiScale .ui-h1, 478 | .uiScale .ui-h2, 479 | .uiScale .ui-h3, 480 | .uiScale .ui-h4, 481 | .uiScale .ui-brand1, 482 | .uiScale .ui-brand2, 483 | .uiScale.ui-pageTitle, 484 | .uiScale .ui-pageTitle, 485 | .u-baseColor--buttonDark, 486 | .u-textColor--brandBlack, 487 | .label--withIcon .svgIcon, 488 | .uiScale-ui--small.ui-body, 489 | .uiScale-ui--small .ui-body, 490 | .uiScale-ui--large.ui-body, 491 | .uiScale-ui--large .ui-body, 492 | .uiScale-ui--large .ui-tab, 493 | .uiScale-ui--small .ui-tab, 494 | .uiScale-ui--regular .ui-tab, 495 | .uiScale-ui--large .ui-caps, 496 | .uiScale-ui--small .ui-caps, 497 | .uiScale-ui--regular .ui-caps, 498 | .notificationsList-note, 499 | .notificationsList-title, 500 | .notificationsList-author, 501 | .notificationsList-milestone, 502 | .popover-inner .popover-title, 503 | .chartTabs .is-active .chartTab, 504 | .uiScale-ui--large.ui-brandTitle, 505 | .uiScale-ui--large .ui-brandTitle, 506 | .uiScale-ui--regular.ui-brandTitle, 507 | .uiScale-ui--regular .ui-brandTitle, 508 | .uiScale-caption--small .ui-captionStrong, 509 | .uiScale-caption--regular .ui-captionStrong, 510 | .u-backgroundTransparentWhiteDarker, 511 | .u-tintSpectrum .u-baseColor--textDarker, 512 | .u-tintSpectrum .svgIcon--logoWordmark, 513 | .u-tintSpectrum .svgIcon--logoWordmark svg, 514 | .u-accentColor--buttonDark.button--filled, 515 | .u-accentColor--buttonDark.button--withChrome.is-active { 516 | color: var(--color_txt_cc) !important; 517 | fill: var(--color_txt_cc) !important; 518 | } 519 | 520 | h1, 521 | h2, 522 | h3, 523 | h4, 524 | h5, 525 | h6 { 526 | color: var(--color_txt_dd) !important; 527 | } 528 | 529 | .button--twitter .button-label, 530 | .button--filled.is-active .button-label, 531 | .button--withChrome.is-active .button-label { 532 | color: var(--color_bkg_0d) !important; 533 | } 534 | 535 | .button, 536 | .list-item, 537 | .buttonSwitch, 538 | .chartTabs li, 539 | .EmbeddedTweet, 540 | .cardChromeless, 541 | .lockedPostHeader, 542 | .graf--blockquote, 543 | .u-borderBlackLightest, 544 | .u-borderBottomGrayLight, 545 | .u-borderBottomLight, 546 | .u-borderBottomLighter, 547 | .u-borderBottomLightest, 548 | .u-borderBottomNormal, 549 | .u-borderBottomWhite15, 550 | .u-borderCardBackground, 551 | .u-borderCardBorder, 552 | .u-borderColorDark, 553 | .u-borderColorDarker, 554 | .u-borderColorLight, 555 | .u-borderColorLighter, 556 | .u-borderColorLightest, 557 | .u-borderColorNormal, 558 | .u-borderWhite, 559 | .u-borderDark, 560 | .u-borderDarker, 561 | .u-borderLeftBleed185:before, 562 | .u-borderLeftBleed250:before, 563 | .u-borderLeftBleed300:before, 564 | .u-borderLeftLighter, 565 | .u-borderLight, 566 | .u-borderLighter, 567 | .u-borderLighterHover:hover, 568 | .u-borderLightest, 569 | .u-borderNormal, 570 | .u-borderRightLighter, 571 | .u-borderRightTransparentWhiteLighter, 572 | .u-borderRightWhite15, 573 | .u-borderTop2, 574 | .u-borderTopLight, 575 | .u-borderTopLighter, 576 | .u-borderTopLightest, 577 | .u-borderTopColorDarker, 578 | .jobs-teams__item:after, 579 | .stats-title--chart, 580 | .table--bordered tr, 581 | .heading-supplemental, 582 | .streamItem-cardInner, 583 | .textInput--underlined, 584 | .u-borderColor--brandSea, 585 | .u-baseColor--buttonDark, 586 | .popover-prePublishBorder, 587 | .u-accentColor--borderDark, 588 | .chartPage-verticalDivider, 589 | .streamItemConversation-divider, 590 | .TwitterCard-container--clickable, 591 | .js-carouselInner section .u-borderBox, 592 | .table-thead--borderLeftBleed650:before, 593 | .u-resetSpectrum .u-baseColor--buttonDark, 594 | .u-tintSpectrum .u-baseColor--borderLight, 595 | .u-tintSpectrum a.u-baseColor--buttonNormal, 596 | .catalogPreview--card .catalogPreview-header, 597 | .heading--borderedTop .heading-tabsItem.is-active, 598 | .heading--borderedBottom .heading-tabsItem.is-active, 599 | .streamItemConversationItem--preview:not(:first-child), 600 | .responsesStream-showOtherResponses.cardChromeless:hover, 601 | .u-tintSpectrum:not(.metabar) .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 602 | .u-tintSpectrum:not(.metabar) .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) { 603 | border-color: var(--color_brd_33) !important; 604 | } 605 | 606 | .chartTabs .chartTab { 607 | border-left-color: var(--color_brd_33) !important; 608 | } 609 | 610 | .u-borderTopLightest80:before, 611 | .u-backgroundTransparentBlackDark, 612 | .postActions .buttonSet-separator, 613 | .postActionsBar .buttonSet-separator { 614 | background-color: var(--color_brd_33) !important; 615 | } 616 | 617 | body .u-accentColor--buttonNormal:hover { 618 | border-color: var(--color_hover) !important; 619 | } 620 | 621 | .list-item:not(.list-item--separator) { 622 | border: 0 !important; 623 | } 624 | 625 | line.bargraph-gridLine { 626 | stroke: var(--color_brd_33) !important; 627 | } 628 | 629 | .popover-inner, 630 | .graf--mixtapeEmbed, 631 | .u-baseColor--boxShadow, 632 | .u-backgroundColorWhite, 633 | .u-boxShadow1px4pxCardBorder, 634 | .u-borderBox img.u-baseColor--boxShadow { 635 | box-shadow: 0 1px 2px var(--color_brd_33), 0 -1px 2px var(--color_brd_33) !important; 636 | } 637 | .u-boxShadow { 638 | box-shadow: 0 1px 7px var(--color_bkg_0d) !important; 639 | border: 0 !important; 640 | } 641 | .popover-arrow:after { 642 | box-shadow: -1px -1px 1px -1px var(--color_brd_33) !important; 643 | } 644 | .popover--top .popover-arrow:after { 645 | box-shadow: 1px 1px 1px 0 var(--color_brd_33) !important; 646 | } 647 | .responsesStream-editor { 648 | box-shadow: none !important; 649 | } 650 | 651 | text, 652 | .button--dark svg, 653 | .overlay-title path, 654 | .button-defaultState svg, 655 | .bargraph-xAxis .bargraph-yAxis, 656 | .u-accentColor--buttonNormal .svgIcon svg, 657 | .u-accentColor--buttonNormal .icon:before { 658 | fill: currentColor !important; 659 | } 660 | 661 | /* accent colors */ 662 | .metabar, 663 | div.u-tintBgColor { 664 | background-color: var(--accent_header) !important; 665 | } 666 | 667 | a, 668 | a.link, 669 | a.link .u-baseColor--textDark, 670 | .link--accent:focus, 671 | .link--accent:hover, 672 | .link--accent:active, 673 | .uiScale .ui-linkObvious, 674 | .uiScale-ui--small .ui-tabActive, 675 | .uiScale-ui--large .ui-tabActive, 676 | .uiScale-ui--regular .ui-tabActive, 677 | .u-accentColor--buttonNormal .icon:before, 678 | .u-tintSpectrum .u-baseColor--iconLight.svgIcon, 679 | .u-tintSpectrum .u-baseColor--iconLight .svgIcon, 680 | .u-tintSpectrum .u-baseColor--buttonLight .svgIcon, 681 | .u-tintSpectrum .u-baseColor--buttonLight .icon:before, 682 | .u-tintSpectrum .u-baseColor--buttonNormal .icon:before, 683 | .u-tintSpectrum .u-baseColor--buttonNormal .svgIcon, 684 | .u-tintSpectrum .u-baseColor--link, 685 | .u-tintSpectrum .u-baseColor--buttonLight, 686 | .u-tintSpectrum .u-baseColor--buttonNormal, 687 | .u-tintSpectrum .u-accentColor--buttonDark, 688 | .u-tintSpectrum .u-accentColor--buttonNormal:not(.clapButton--largePill), 689 | body .u-accentColor--buttonNormal:not(.clapButton--largePill), 690 | .button .svgIcon:not(.svgIcon--clap):not(.svgIcon--clapFilled), 691 | .suggestionSidebar .button.is-active:not(.button--withIcon) .button-activeState, 692 | .u-tintSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 693 | .u-tintSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) { 694 | color: var(--accent_link) !important; 695 | fill: var(--accent_link) !important; 696 | border-color: var(--accent_link) !important; 697 | } 698 | 699 | a:focus, 700 | a:hover, 701 | a:active, 702 | a.link:hover, 703 | .button--dark:hover svg, 704 | a.button:hover, 705 | a.button:hover .svgIcon, 706 | a.button:focus .svgIcon, 707 | a.button:active .svgIcon, 708 | a.button.is-active .svgIcon, 709 | li .button--chromeless:hover { 710 | color: var(--accent_hover) !important; 711 | fill: var(--accent_hover) !important; 712 | } 713 | 714 | body.is-withMagicUnderlines .markup--anchor { 715 | background-image: linear-gradient(to bottom, var(--accent_underline) 50%, rgba(0, 0, 0, 0) 50%) !important; 716 | /* including underline settings to prevent them being overridden */ 717 | background-repeat: repeat-x !important; 718 | background-size: 2px .1em !important; 719 | background-position: 0 1.07em !important; 720 | } 721 | 722 | .u-tintSpectrum .u-baseColor--link:hover, 723 | .u-tintSpectrum .u-baseColor--buttonNormal:hover, 724 | .u-tintSpectrum .u-accentColor--buttonDark:hover, 725 | .u-tintSpectrum .u-baseColor--link.link--darken:hover, 726 | .u-tintSpectrum .u-baseColor--link.link--darken:focus, 727 | .u-tintSpectrum .u-baseColor--link.link--darken:active, 728 | .u-accentColor--textNormal.u-accentColor--textDarken:hover, 729 | .u-tintSpectrum .u-baseColor--link.link--darkenOnHover:hover, 730 | .u-tintSpectrum .u-accentColor--buttonNormal:not(.clapButton--largePill):hover { 731 | color: var(--accent_hover) !important; 732 | fill: var(--accent_hover) !important; 733 | border-color: var(--accent_hover) !important; 734 | } 735 | 736 | .markup--user, 737 | .u-textColorNavy, 738 | .u-textColor--brandSage, 739 | .u-textColor--brandSea, 740 | .u-textColor--brandSky, 741 | .u-textColor--brandPlum, 742 | .u-textColor--brandRose, 743 | .u-textColor--brandSand, 744 | .u-textColorGreenNormal, 745 | .u-accentColor--textDark, 746 | body .u-accentColor--textNormal, 747 | .u-accentColor--borderNormal, 748 | .u-accentColor--iconNormal.svgIcon, 749 | .u-accentColor--iconNormal .svgIcon, 750 | body .u-accentColor--buttonNormal .svgIcon, 751 | .u-baseColor--iconNormal.avatar-halo, 752 | .u-tintSpectrum .svgIcon--logoMonogram, 753 | .u-tintSpectrum .svgIcon--logoMonogram svg, 754 | .clapButton:disabled .svgIcon--clap, 755 | .clapButton:disabled .svgIcon--clapFilled, 756 | .u-accentColor--buttonNormal .svgIcon--clap, 757 | .elevate-caps.u-accentColor--textNormal, 758 | .elevate-accent.u-accentColor--textNormal { 759 | color: var(--accent_main) !important; 760 | fill: var(--accent_main) !important; 761 | } 762 | 763 | .u-strokeGreen { 764 | stroke: var(--accent_main) !important; 765 | } 766 | .u-strokeRose { 767 | stroke: var(--color_txt_88) !important; 768 | } 769 | 770 | .button:hover, 771 | .tags--postTags > a:hover, 772 | .tags--postTags > li:hover, 773 | .tags--postTags > .button:hover, 774 | .tags--postTags .tag-button:hover, 775 | .u-accentColor--borderNormal, 776 | body .postArticle.is-withAccentColors .markup--user, 777 | body .postArticle.is-withAccentColors .markup--query, 778 | .u-accentColor--buttonNormal:not(.clapButton--largePill) { 779 | color: var(--accent_main) !important; 780 | border-color: var(--accent_main) !important; 781 | } 782 | 783 | .u-accentColor--buttonDark.button--filled, 784 | .u-accentColor--buttonDark.button--withChrome.is-active, 785 | .u-accentColor--fillWhenActive.is-active, 786 | .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill), 787 | .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) { 788 | border-color: var(--accent_main) !important; 789 | } 790 | 791 | ::selection { 792 | background-color: var(--accent_highlight) !important; 793 | color: var(--color_txt_ee) !important; 794 | } 795 | ::-moz-selection { 796 | background-color: var(--accent_highlight) !important; 797 | color: var(--color_txt_ee) !important; 798 | } 799 | 800 | .loadingBar, 801 | .markup--quote, 802 | .markup--quote strong, 803 | .markup--highlight, 804 | .quoteItem .markup--highlight, 805 | .u-accentColor--highlightFaint, 806 | body.is-withMagicUnderlines .markup--quote.is-me, 807 | body.is-withMagicUnderlines .markup--quote.is-other, 808 | body.is-withMagicUnderlines .markup--quote .markup--anchor, 809 | html body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-me, 810 | html body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-other { 811 | background: var(--accent_highlight) !important; 812 | border-color: var(--accent_highlight) !important; 813 | color: var(--accent_highlight_text) !important; 814 | } 815 | 816 | .markup--quote:hover { 817 | filter: brightness(1.2) !important; 818 | -webkit-filter: brightness(1.2) !important; 819 | } 820 | 821 | .drawer-image, 822 | .header__logo, 823 | .svgIcon--lock, 824 | /* logo links */ 825 | a.link > img.u-maxSizeFullWidth.u-maxSizeFullHeight, 826 | /* inline image used as a horizontal separator */ 827 | .graf-image[data-height="20"], 828 | .graf-image[data-height="22"] { 829 | filter: invert(90.5%) hue-rotate(180deg) !important; 830 | } 831 | 832 | /* hide white background */ 833 | .creditCardForm-icon, 834 | .creditCardForm-ccImage { 835 | border-radius: 3px !important; 836 | } 837 | 838 | /* Google prettyprint - Tomorrow Night */ 839 | .prettyprint { 840 | background: #1d1f21 !important; 841 | font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace !important; 842 | line-height: 1.4em !important; 843 | padding: 10px !important; 844 | border: 0 !important; 845 | } 846 | .pln { color: #c5c8c6 !important; } 847 | ol.linenums { 848 | margin-top: 0 !important; 849 | margin-bottom: 0 !important; 850 | } 851 | @media screen { 852 | .str { color: #b5bd68 !important; } 853 | .kwd { color: #b294bb !important; } 854 | .com { color: #969896 !important; } 855 | .typ { color: #81a2be !important; } 856 | .lit { color: #de935f !important; } 857 | .pun { color: #c5c8c6 !important; } 858 | .opn { color: #c5c8c6 !important; } 859 | .clo { color: #c5c8c6 !important; } 860 | .tag { color: #c66 !important; } 861 | .atn { color: #de935f !important; } 862 | .atv { color: #8abeb7 !important; } 863 | .dec { color: #de935f !important; } 864 | .var { color: #c66 !important; } 865 | .fun { color: #81a2be !important; } 866 | } 867 | @media print, projection { 868 | .str { color: #060 !important; } 869 | .kwd { color: #006 !important; font-weight: 700 !important; } 870 | .com { color: #600 !important; font-style: italic !important; } 871 | .typ { color: #404 !important; font-weight: 700 !important; } 872 | .lit { color: #044 !important; } 873 | .clo, .opn, .pun { color: #440 !important; } 874 | .tag { color: #006 !important; font-weight: 700 !important; } 875 | .atn { color: #404 !important; } 876 | .atv { color: #060 !important; } 877 | } 878 | 879 | /* animations */ 880 | @keyframes black-pulse-09 { 881 | 0% { transform: scale(1); fill: var(--color_txt_aa) !important; } 882 | 15% { transform: scale(.9) !important; } 883 | 100% { transform: scale(1); fill: var(--color_txt_aa) !important; } 884 | } 885 | 886 | @keyframes pulse-shadow { 887 | 0% { box-shadow: 0 0 0 0 var(--accent_main) !important; } 888 | 70% { box-shadow: 0 0 5px 10px rgba(255, 255, 255, 0) !important; } 889 | 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0) !important; } 890 | } 891 | 892 | @keyframes pulse-shadow--onboarding { 893 | 0% { box-shadow: 0 0 0 0 var(--accent_main) !important; } 894 | 30% { box-shadow: 0 0 0 0 var(--accent_main) !important; } 895 | 80% { box-shadow: 0 0 5px 10px rgba(255, 255, 255, 0) !important; } 896 | 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0) !important; } 897 | } 898 | 899 | /* Gist Syntax highlighting - GitHub Twilight syntax theme */ 900 | body, 901 | .gist .highlight { 902 | background-color: transparent !important; 903 | } 904 | 905 | .gist .gist-data, 906 | .gist .gist-meta { 907 | background-color: var(--color_bkg_1b) !important; 908 | } 909 | 910 | .gist .gist-data, 911 | .gist .gist-file { 912 | border-color: var(--color_brd_33) !important; 913 | } 914 | 915 | .gist .blob-code-inner, 916 | .gist .pl-entl, .gist .pl-s2, .gist .pl-smi, .gist .pl-smp { 917 | color: var(--color_txt_cc) !important; 918 | } 919 | 920 | .gist .pl-c, .gist .pl-c span, .gist .pl-pdc { 921 | color: #5f5a60 !important; 922 | } 923 | 924 | .gist .pl-c1, .gist .pl-cce, .gist .pl-cn, .gist .pl-coc, .gist .pl-enc, 925 | .gist .pl-ens, .gist .pl-kos, .gist .pl-kou, .gist .pl-mdr, .gist .pl-mh, 926 | .gist .pl-mh .pl-pdh, .gist .pl-mq, .gist .pl-pdc1, .gist .pl-pde, 927 | .gist .pl-pse, .gist .pl-pse .pl-s2, .gist .pl-scp, .gist .pl-vi { 928 | color: #cf6a4c !important; 929 | } 930 | 931 | .gist .pl-entm, .gist .pl-eoac, .gist .pl-eoac .pl-pde, .gist .pl-mai .pl-sf, 932 | .gist .pl-mm, .gist .pl-pdv, .gist .pl-som, .gist .pl-sr, .gist .pl-stj, 933 | .gist .pl-vpf { 934 | color: #7587a6 !important; 935 | } 936 | 937 | .gist .pl-e, .gist .pl-ef, .gist .pl-en, .gist .pl-enf, .gist .pl-enm, 938 | .gist .pl-entc, .gist .pl-eoi, .gist .pl-smc, .gist .pl-vo, .gist .pl-enti { 939 | color: #ac885b !important; 940 | } 941 | 942 | .gist .pl-ent, .gist .pl-eoa, .gist .pl-eoai, .gist .pl-eoai .pl-pde, 943 | .gist .pl-k, .gist .pl-ko, .gist .pl-kolp, .gist .pl-mc, .gist .pl-mp .pl-s3, 944 | .gist .pl-mr, .gist .pl-ms, .gist .pl-sok, .gist .pl-sra, .gist .pl-src, 945 | .gist .pl-sre, .gist .pl-st, .gist .pl-mi, .gist .pl-pdi { 946 | color: #cda869 !important; 947 | } 948 | 949 | .gist .pl-mp { 950 | color: #c5af75 !important; 951 | } 952 | 953 | .gist .pl-mp1 .pl-sf, .gist .pl-s3, .gist .pl-sc, .gist .pl-sf { 954 | color: #dad085 !important; 955 | } 956 | 957 | .gist .pl-s { 958 | color: #f9ee98 !important; 959 | } 960 | 961 | .gist .pl-stp, .gist .pl-sv, .gist .pl-v { 962 | color: #9b859d !important; 963 | } 964 | 965 | .gist .pl-mri, .gist .pl-va, .gist .pl-vpu { 966 | color: teal !important; 967 | } 968 | 969 | .gist .pl-mdht, .gist .pl-mi1 { 970 | color: #55a532 !important; 971 | background: #020 !important; 972 | } 973 | 974 | .gist .pl-cos, .gist .pl-ml, .gist .pl-pds, .gist .pl-s1, .gist .pl-sol, 975 | .gist .pl-mb, .gist .pl-pdb { 976 | color: #8f9d6a !important; 977 | } 978 | 979 | .gist .pl-md, .gist .pl-mdhf { 980 | color: #bd2c00 !important; 981 | background: #200 !important; 982 | } 983 | 984 | .gist .pl-mdh, .gist .pl-mdi { 985 | color: #7587a6 !important; 986 | } 987 | 988 | .gist .pl-id { 989 | background-color: #a31515 !important; 990 | color: var(--color_txt_ee) !important; 991 | } 992 | 993 | .gist .pl-ib { 994 | background-color: #f93 !important; 995 | } 996 | 997 | .gist .pl-ii, .gist .pl-ii .pl-cce { 998 | background-color: #df5000 !important; 999 | color: var(--color_txt_ee) !important; 1000 | } 1001 | 1002 | .gist .pl-iu { 1003 | background-color: #b4b7b4 !important; 1004 | } 1005 | 1006 | .gist .pl-mo { 1007 | color: #969896 !important; 1008 | } 1009 | .gist .pl-ms1 { 1010 | background-color: #f5f5f5 !important; 1011 | } 1012 | 1013 | } 1014 | 1015 | @-moz-document regexp("^https://medium.com/@[^/]+$"), url-prefix("https://medium.com/me/") { 1016 | /* UserCSS only: weird profile pages - https://medium.com/@wowmotty */ 1017 | nav.m, div[data-reactroot] > nav { 1018 | background: var(--accent_header) !important; 1019 | } 1020 | 1021 | a { 1022 | color: var(--accent_main) !important; 1023 | border-color: var(--accent_main) !important; 1024 | } 1025 | 1026 | a svg { 1027 | fill: currentColor !important; 1028 | filter: none !important; 1029 | } 1030 | 1031 | ul, div.k, div[style*="left"]:after { 1032 | background: var(--color_bkg_11) !important; 1033 | border-color: var(--color_brd_33) !important; 1034 | } 1035 | 1036 | li, div[style*="absolute"] { 1037 | border-color: var(--color_brd_33) !important; 1038 | } 1039 | 1040 | h1, h2, h3, h4, h5, h6, div, span { 1041 | color: var(--color_txt_aa) !important; 1042 | } 1043 | 1044 | svg { 1045 | filter: invert(100%) !important; 1046 | } 1047 | } 1048 | 1049 | -------------------------------------------------------------------------------- /gist.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color_bkg_0d: #0d0d0d; 3 | --color_bkg_11: #111; 4 | --color_bkg_1b: #1b1b1b; 5 | 6 | --color_brd_33: #333; 7 | 8 | --color_txt_88: #888; 9 | --color_txt_aa: #aaa; 10 | --color_txt_cc: #ccc; 11 | --color_txt_dd: #ddd; 12 | --color_txt_ee: #eee; 13 | } 14 | /* Gist Syntax highlighting - GitHub Twilight syntax theme */ 15 | body, 16 | .gist .highlight { 17 | background-color: transparent !important; 18 | } 19 | 20 | .gist .gist-data, 21 | .gist .gist-meta { 22 | background-color: var(--color_bkg_1b) !important; 23 | } 24 | 25 | .gist .gist-data, 26 | .gist .gist-file { 27 | border-color: var(--color_brd_33) !important; 28 | } 29 | 30 | .gist .blob-code-inner, 31 | .gist .pl-entl, .gist .pl-s2, .gist .pl-smi, .gist .pl-smp { 32 | color: var(--color_txt_cc) !important; 33 | } 34 | 35 | .gist .pl-c, .gist .pl-c span, .gist .pl-pdc { 36 | color: #5f5a60 !important; 37 | } 38 | 39 | .gist .pl-c1, .gist .pl-cce, .gist .pl-cn, .gist .pl-coc, .gist .pl-enc, 40 | .gist .pl-ens, .gist .pl-kos, .gist .pl-kou, .gist .pl-mdr, .gist .pl-mh, 41 | .gist .pl-mh .pl-pdh, .gist .pl-mq, .gist .pl-pdc1, .gist .pl-pde, 42 | .gist .pl-pse, .gist .pl-pse .pl-s2, .gist .pl-scp, .gist .pl-vi { 43 | color: #cf6a4c !important; 44 | } 45 | 46 | .gist .pl-entm, .gist .pl-eoac, .gist .pl-eoac .pl-pde, .gist .pl-mai .pl-sf, 47 | .gist .pl-mm, .gist .pl-pdv, .gist .pl-som, .gist .pl-sr, .gist .pl-stj, 48 | .gist .pl-vpf { 49 | color: #7587a6 !important; 50 | } 51 | 52 | .gist .pl-e, .gist .pl-ef, .gist .pl-en, .gist .pl-enf, .gist .pl-enm, 53 | .gist .pl-entc, .gist .pl-eoi, .gist .pl-smc, .gist .pl-vo, .gist .pl-enti { 54 | color: #ac885b !important; 55 | } 56 | 57 | .gist .pl-ent, .gist .pl-eoa, .gist .pl-eoai, .gist .pl-eoai .pl-pde, 58 | .gist .pl-k, .gist .pl-ko, .gist .pl-kolp, .gist .pl-mc, .gist .pl-mp .pl-s3, 59 | .gist .pl-mr, .gist .pl-ms, .gist .pl-sok, .gist .pl-sra, .gist .pl-src, 60 | .gist .pl-sre, .gist .pl-st, .gist .pl-mi, .gist .pl-pdi { 61 | color: #cda869 !important; 62 | } 63 | 64 | .gist .pl-mp { 65 | color: #c5af75 !important; 66 | } 67 | 68 | .gist .pl-mp1 .pl-sf, .gist .pl-s3, .gist .pl-sc, .gist .pl-sf { 69 | color: #dad085 !important; 70 | } 71 | 72 | .gist .pl-s { 73 | color: #f9ee98 !important; 74 | } 75 | 76 | .gist .pl-stp, .gist .pl-sv, .gist .pl-v { 77 | color: #9b859d !important; 78 | } 79 | 80 | .gist .pl-mri, .gist .pl-va, .gist .pl-vpu { 81 | color: teal !important; 82 | } 83 | 84 | .gist .pl-mdht, .gist .pl-mi1 { 85 | color: #55a532 !important; 86 | background: #020 !important; 87 | } 88 | 89 | .gist .pl-cos, .gist .pl-ml, .gist .pl-pds, .gist .pl-s1, .gist .pl-sol, 90 | .gist .pl-mb, .gist .pl-pdb { 91 | color: #8f9d6a !important; 92 | } 93 | 94 | .gist .pl-md, .gist .pl-mdhf { 95 | color: #bd2c00 !important; 96 | background: #200 !important; 97 | } 98 | 99 | .gist .pl-mdh, .gist .pl-mdi { 100 | color: #7587a6 !important; 101 | } 102 | 103 | .gist .pl-id { 104 | background-color: #a31515 !important; 105 | color: var(--color_txt_ee) !important; 106 | } 107 | 108 | .gist .pl-ib { 109 | background-color: #f93 !important; 110 | } 111 | 112 | .gist .pl-ii, .gist .pl-ii .pl-cce { 113 | background-color: #df5000 !important; 114 | color: var(--color_txt_ee) !important; 115 | } 116 | 117 | .gist .pl-iu { 118 | background-color: #b4b7b4 !important; 119 | } 120 | 121 | .gist .pl-mo { 122 | color: #969896 !important; 123 | } 124 | .gist .pl-ms1 { 125 | background-color: #f5f5f5 !important; 126 | } 127 | -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon128.png -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon19.png -------------------------------------------------------------------------------- /icons/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon32.png -------------------------------------------------------------------------------- /icons/icon38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon38.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/icons/icon48.png -------------------------------------------------------------------------------- /images/Darker-Medium-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/Darker-Medium-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/images/Darker-Medium-options.png -------------------------------------------------------------------------------- /images/Darker-Medium-toggle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/images/Darker-Medium-toggle.gif -------------------------------------------------------------------------------- /images/Medium-site-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Darker-Medium/02418792379620170bf17c78ec4199b7a89b55f6/images/Medium-site-screenshot.png -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | /* global chrome */ 2 | "use strict"; 3 | 4 | // Keyboard shortcut 5 | function keyboardShortcut() { 6 | enableAllCurrentTabs("toggle"); 7 | } 8 | 9 | if ("commands" in chrome) { 10 | chrome.commands.onCommand.removeListener(keyboardShortcut); 11 | chrome.commands.onCommand.addListener(keyboardShortcut); 12 | } 13 | 14 | /** 15 | * Modified from 16 | * https://github.com/bfred-it/webext-inject-on-install 17 | */ 18 | function showErrors() { 19 | if (chrome.runtime.lastError) { 20 | console.error(chrome.runtime.lastError); 21 | } 22 | } 23 | 24 | function loopTabs(callback) { 25 | chrome.runtime.getManifest().content_scripts.forEach(script => { 26 | const url = script.matches, 27 | validUrl = /^http/; 28 | chrome.tabs.query({url}, tabs => { 29 | tabs.forEach(tab => { 30 | // Don't mess with file://, chrome://, etc. 31 | if (validUrl.test(tab.url)) { 32 | callback(tab, script); 33 | } 34 | }); 35 | }); 36 | }); 37 | } 38 | 39 | function enableAllCurrentTabs(mode) { 40 | loopTabs((tab, script) => { 41 | const allFrames = script.all_frames; 42 | (script.js || []).forEach(file => { 43 | chrome.tabs.executeScript(tab.id, {allFrames, file}, showErrors); 44 | }); 45 | if (mode) { 46 | chrome.tabs.sendMessage(tab.id, {text: mode}); 47 | } 48 | }); 49 | } 50 | 51 | setTimeout(() => { 52 | enableAllCurrentTabs("update"); 53 | }, 500); 54 | 55 | // Toggle all tabs 56 | chrome.commands.onCommand.addListener(() => { 57 | loopTabs(tab => { 58 | chrome.tabs.sendMessage(tab.id, {text: "toggleAll"}); 59 | }); 60 | }); 61 | 62 | // Only toggle current tab 63 | chrome.browserAction.onClicked.addListener(tab => { 64 | chrome.tabs.sendMessage(tab.id, {text: "toggleTab"}); 65 | }); 66 | 67 | chrome.runtime.onMessage.addListener((request, sender) => { 68 | // Add Google Prettify 69 | if (request.prettify) { 70 | chrome.tabs.executeScript(sender.tab.id, { 71 | file: "run_prettify.js" 72 | }); 73 | } 74 | }); 75 | -------------------------------------------------------------------------------- /js/content.js: -------------------------------------------------------------------------------- 1 | /* Darker Medium 2 | * Copyright © 2018 Rob Garrison 3 | * License: MIT 4 | */ 5 | /* global chrome */ 6 | (() => { 7 | "use strict"; 8 | 9 | // Look for "medium-com" in the prefix attribute 10 | const IS_MEDIUM = /medium-com/, 11 | // Stylesheet IDs 12 | THEME = "darker-medium-theme", 13 | VARS = THEME + "-variables", 14 | // Attribute added to stylesheet when moved after on page load 15 | MOVED = "data-" + THEME + "-moved", 16 | // Attribute added to after initialization 17 | INIT = "data-" + THEME + "-initialized", 18 | 19 | PRETTYPRINT = ".section-content pre:not(.prettyprint)", 20 | MUTATION_DEBOUNCE = 500, 21 | 22 | SETTINGS = { 23 | enabled: true, 24 | // Accent colors (comments show defaults) 25 | // styles added by build script (modify `build/settings.json`, not here) 26 | styles: { 27 | /* eslint-disable quote-props */ 28 | /* BUILD:SETTINGS_START */ 29 | "header": "#1d1d1d", 30 | "main": "#7AA8D6", 31 | "highlight": "#7AA8D6", 32 | "link": "#dddddd", 33 | "hover": "#dddddd", 34 | "underline": 1, 35 | "footer": 0 36 | /* BUILD:SETTINGS_END */ 37 | /* eslint-enable quote-props */ 38 | } 39 | }, 40 | HEX_REGEX = /[^0-9A-F]/gi, // Remove non hex characters 41 | 42 | // Underline style alpha value copied from medium source css 43 | // See https://medium.design/crafting-link-underlines-on-medium-7c03a9274f9 44 | ALPHA = 0.68, // Magic underline alpha value 45 | 46 | // Opera doesn't support sync 47 | STORAGE = chrome.storage[chrome.storage.sync ? "sync" : "local"]; 48 | 49 | let timerCheck, 50 | timerStorage; 51 | 52 | function checkPage(mode) { 53 | clearTimeout(timerCheck); 54 | // `document.head` may not be available at document-start 55 | if (!document.head) { 56 | timerCheck = setTimeout(() => { 57 | checkPage(mode); 58 | }, 10); 59 | return; 60 | } 61 | // Medium posts have a that looks something like this: 62 | // 63 | if (IS_MEDIUM.test(document.head.getAttribute("prefix"))) { 64 | initDarkerMedium(mode); 65 | } else if (isMediumIframe()) { 66 | insertInIframe(); 67 | } 68 | } 69 | 70 | function initDarkerMedium(mode) { 71 | const hasInitialized = document.head.getAttribute(INIT) === "true"; 72 | // Add attribute to HTML for iframe to check parent 73 | document.documentElement.setAttribute(THEME, true); 74 | document.head.setAttribute(INIT, true); 75 | getStorage().then(values => { 76 | const isEnabled = values.enabled ? "enable" : "disable"; 77 | if ( 78 | hasInitialized && 79 | $(THEME) && 80 | mode.indexOf("toggle") > -1 // Check for toggleTab or toggleAll 81 | ) { 82 | toggleStylesheet(mode); 83 | } else if (!hasInitialized) { 84 | addStylesheet(isEnabled); 85 | } 86 | // Inserts style before the link because it contains the css 87 | // variable definitions 88 | addVars(values.styles); 89 | initSyntaxHighlighting(); 90 | }); 91 | } 92 | 93 | function processStyles(styles) { 94 | let accents = "\n"; 95 | Object.keys(SETTINGS.styles).forEach(name => { 96 | let style = styles[name]; 97 | if (name === "footer") { 98 | accents += ` --hide_footer: ${style ? "none" : "block"};`; 99 | return; // eslint-disable-next-line no-else-return 100 | } else if (name === "underline") { 101 | // If underline is disabled, set the alpha channel to zero to make it 102 | // transparent 103 | const alpha = style === true ? ALPHA : 0; 104 | style = hex2rgba(styles.link, alpha); 105 | } else if (name === "highlight" || name === "main") { 106 | // Add text contrast color to match the selected background color 107 | accents += ` --accent_${name}_text: ${calcContrast(style)};\n`; 108 | } else { 109 | style = cleanStyle(style, {hash: true}); 110 | } 111 | accents += ` --accent_${name}: ${style};\n`; 112 | }); 113 | return accents; 114 | } 115 | 116 | function cleanStyle(string, options = {}) { 117 | const hex = string.replace(HEX_REGEX, ""); 118 | return options.hash ? "#" + hex : hex; 119 | } 120 | 121 | // Always expects a 6-digit hex value & alpha fractional value 122 | // Modified from https://github.com/sindresorhus/hex-rgb (MIT) 123 | function hex2rgba(hex, alpha = 1) { 124 | const num = parseInt(cleanStyle(hex), 16); 125 | return `rgba(${num >> 16}, ${num >> 8 & 255}, ${num & 255}, ${alpha})`; 126 | } 127 | 128 | // Calculate contrasting text color for the given background color 129 | // https://24ways.org/2010/calculating-color-contrast/ 130 | function calcContrast(hex) { 131 | hex = cleanStyle(hex); 132 | const r = parseInt(hex.substr(0, 2), 16), 133 | g = parseInt(hex.substr(2, 2), 16), 134 | b = parseInt(hex.substr(4, 2), 16), 135 | yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; 136 | return yiq >= 128 ? "black" : "white"; 137 | } 138 | 139 | function isMediumIframe() { 140 | try { 141 | if ( 142 | window !== parent && 143 | // Look for Darker Medium theme attribute 144 | parent.document.documentElement.getAttribute(THEME) 145 | ) { 146 | return true; 147 | } 148 | } catch (error) {} 149 | return false; 150 | } 151 | 152 | function onDOMLoaded() { 153 | return new Promise(resolve => { 154 | if (document.readyState === "loading") { 155 | document.addEventListener("DOMContentLoaded", resolve, {once: true}); 156 | } else { 157 | resolve(); 158 | } 159 | }); 160 | } 161 | 162 | function iframeLoaded() { 163 | let timer, 164 | counter = 0; 165 | return new Promise((resolve, reject) => { 166 | const checkComplete = () => { 167 | counter++; 168 | clearTimeout(timer); 169 | timer = setTimeout(() => { 170 | if (counter > 100) { 171 | reject(); 172 | } 173 | if (document.readyState !== "complete") { 174 | return checkComplete(); 175 | } 176 | resolve(); 177 | }, 100); 178 | }; 179 | checkComplete(); 180 | }); 181 | } 182 | 183 | function insertInIframe() { 184 | iframeLoaded().then(() => { 185 | if (document.querySelector("script[src*='gist']")) { 186 | getStorage().then(values => { 187 | const link = createStyle({ 188 | link: document.createElement("link"), 189 | id: THEME + "-gist", 190 | mode: values.enabled ? "enable" : "disable", 191 | style: "gist.css" 192 | }); 193 | addToDOM(link); 194 | }); 195 | } 196 | }).catch(() => { 197 | console.log("Unable to process iframe", window.location.href); 198 | }); 199 | } 200 | 201 | function initSyntaxHighlighting() { 202 | onDOMLoaded().then(() => { 203 | addSyntaxHighlighting(); 204 | let debounce; 205 | new MutationObserver(() => { 206 | clearTimeout(debounce); 207 | debounce = setTimeout(() => { 208 | if (document.querySelector(PRETTYPRINT)) { 209 | addSyntaxHighlighting(); 210 | } 211 | }, MUTATION_DEBOUNCE); 212 | }).observe(document.body, { 213 | attributes: true 214 | }); 215 | }); 216 | } 217 | 218 | function addSyntaxHighlighting() { 219 | const pres = [...document.querySelectorAll(PRETTYPRINT)]; 220 | if (pres.length > 0) { 221 | pres.forEach(el => { 222 | el.classList.add("prettyprint"); 223 | }); 224 | chrome.runtime.sendMessage({prettify: true}); 225 | } 226 | } 227 | 228 | function addVars(styles) { 229 | let el = $(VARS); 230 | if (!el) { 231 | el = document.createElement("style"); 232 | el.id = VARS; 233 | addToDOM(el); 234 | } 235 | el.textContent = `:root { ${processStyles(styles)} }`; 236 | } 237 | 238 | function createStyle({link, id, style, mode}) { 239 | link.id = id; 240 | link.disabled = mode === "disable"; 241 | link.rel = "stylesheet"; 242 | link.href = chrome.extension.getURL(style); 243 | return link; 244 | } 245 | 246 | function addStylesheet(mode) { 247 | const el = $(THEME), 248 | $link = createStyle({ 249 | link: el ? el : document.createElement("link"), 250 | id: THEME, 251 | mode, 252 | style: "style.css" 253 | }); 254 | // Add to DOM (after ) 255 | addToDOM($link); 256 | if (document.readyState !== "loading") { 257 | // Add to DOM (after ) 258 | addToDOM($link); 259 | } else { 260 | // Add to DOM (after ) 261 | onDOMLoaded().then(() => addToDOM($link)); 262 | } 263 | } 264 | 265 | // Mode is "enable", "disable", "toggleAll" or "toggleTab" 266 | function toggleStylesheet(mode) { 267 | // Toggle the link, not the variables style 268 | const el = $(THEME); 269 | if (el) { 270 | if (mode.indexOf("toggle") > -1) { 271 | mode = el.disabled ? "" : "disable"; 272 | } 273 | el.disabled = mode === "disable"; 274 | // Don't update storage if only a single tab is being toggled 275 | if (mode !== "toggleTab") { 276 | getStorage().then(values => { 277 | values.enabled = mode !== "disable"; 278 | setStorage(values); 279 | }); 280 | } 281 | } 282 | } 283 | 284 | function addToDOM(el) { 285 | const doc = el.ownerDocument; 286 | // Don't add to DOM if already attached; 287 | // Ensure that variables are *always* defined before the style 288 | if (!el.parentNode || el.id === VARS) { 289 | // Add after tag 290 | doc.head.parentNode.insertBefore(el, doc.head.nextSibling); 291 | } else if (doc.body && !el.getAttribute(MOVED)) { 292 | // Called on window load, move style after tag 293 | doc.body.parentNode.insertBefore(el, doc.body.nextSibling); 294 | el.setAttribute(MOVED, "true"); 295 | } 296 | } 297 | 298 | function onStorageChange(changes) { 299 | // Throttle storage change 300 | clearTimeout(timerStorage); 301 | timerStorage = setTimeout(() => { 302 | if (changes.enabled) { 303 | toggleStylesheet(changes.enabled.newValue ? "enable" : "disable"); 304 | } else { 305 | addVars(changes.styles.newValue); 306 | } 307 | }, 250); 308 | } 309 | 310 | function onMsg(message) { 311 | checkPage(message.text); 312 | } 313 | 314 | function getStorage() { 315 | return new Promise(resolve => { 316 | STORAGE.get(SETTINGS, data => resolve(data)); 317 | }); 318 | } 319 | 320 | function setStorage(data) { 321 | return new Promise(resolve => { 322 | STORAGE.set(data, () => resolve(data)); 323 | }); 324 | } 325 | 326 | function $(selector) { 327 | return document.getElementById(selector); 328 | } 329 | 330 | chrome.runtime.onMessage.removeListener(onMsg); 331 | chrome.runtime.onMessage.addListener(onMsg); 332 | chrome.storage.onChanged.removeListener(onStorageChange); 333 | chrome.storage.onChanged.addListener(onStorageChange); 334 | 335 | checkPage("init"); 336 | })(); 337 | -------------------------------------------------------------------------------- /js/run_prettify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright (C) 2013 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview 20 | *
21 | * Looks at query parameters to decide which language handlers and style-sheets 22 | * to load. 23 | * 24 | * Query Parameter Format Effect Default 25 | * +------------------+---------------+------------------------------+--------+ 26 | * | autorun= | true | false | If true then prettyPrint() | "true" | 27 | * | | | is called on page load. | | 28 | * +------------------+---------------+------------------------------+--------+ 29 | * | lang= | language name | Loads the language handler | Can | 30 | * | | | named "lang-.js". | appear | 31 | * | | | See available handlers at | many | 32 | * | | | https://github.com/google/ | times. | 33 | * | | | code-prettify/tree/master/ | | 34 | * | | | src | | 35 | * +------------------+---------------+------------------------------+--------+ 36 | * | skin= | skin name | Loads the skin stylesheet | none. | 37 | * | | | named ".css". | | 38 | * | | | https://cdn.rawgit.com/ | | 39 | * | | | google/code-prettify/master/ | | 40 | * | | | styles/index.html | | 41 | * +------------------+---------------+------------------------------+--------+ 42 | * | callback= | JS identifier | When "prettyPrint" finishes | none | 43 | * | | | window.exports[js_ident] is | | 44 | * | | | called. | | 45 | * | | | The callback must be under | | 46 | * | | | exports to reduce the risk | | 47 | * | | | of XSS via query parameter | | 48 | * | | | injection. | | 49 | * +------------------+---------------+------------------------------+--------+ 50 | * 51 | * Exmaples 52 | * .../run_prettify.js?lang=css&skin=sunburst 53 | * 1. Loads the CSS language handler which can be used to prettify CSS 54 | * stylesheets, HTML