├── .gitignore ├── README.md ├── TODO ├── icon128.png ├── icon16.png ├── icon48.png ├── jsconfig.json ├── manifest.json ├── package.json ├── scripts └── production.js ├── src ├── assets │ └── images │ │ ├── Logo.svg │ │ ├── favicon.png │ │ ├── icon-dropper.svg │ │ ├── icon-pencil.svg │ │ ├── icon-thumbs-down.svg │ │ ├── icon-thumbs-up.svg │ │ ├── icon-trash.svg │ │ ├── play_music.svg │ │ ├── play_music_logo_dark.png │ │ ├── play_music_logo_light.png │ │ └── sprites │ │ ├── ani_equalizer_white.gif │ │ ├── ani_loading_white.gif │ │ └── equalizer_white.png ├── background.js ├── components │ ├── Button.js │ ├── Checkbox.js │ ├── Grid.js │ ├── Icons │ │ ├── IconCopy.js │ │ ├── IconDropper.js │ │ ├── IconGear.js │ │ ├── IconPencil.js │ │ ├── IconTrash.js │ │ └── SvgIcon.js │ ├── Modal.container.js │ ├── Modals │ │ ├── AlertModal.js │ │ ├── ColorDeleteModal.js │ │ ├── ColorPickerModal.js │ │ ├── ConfirmModal.js │ │ ├── ModalWrapper.js │ │ ├── ModalWrapper.statics.js │ │ ├── ModalWrapper.styled.js │ │ ├── NotificationModal.js │ │ ├── NotificationModal.styles.js │ │ ├── ThemeDeleteModal.js │ │ └── ThemePickerModal.js │ ├── Options │ │ ├── Option │ │ │ ├── Option.styled.js │ │ │ ├── OptionCheckbox.js │ │ │ ├── OptionString.js │ │ │ ├── OptionThemes.js │ │ │ └── index.js │ │ ├── Options.container.js │ │ ├── Options.js │ │ ├── Options.styled.js │ │ ├── Section.js │ │ └── index.js │ ├── PlayMidnight.js │ ├── PlayMidnight.styled.js │ ├── PlayMidnightLogo.js │ ├── PlayMusicLogo.js │ ├── Root.js │ ├── ThemePreview.js │ ├── Toast.container.js │ └── Toasts │ │ ├── AlertToast.js │ │ ├── NotificationToast.js │ │ ├── SuccessToast.js │ │ └── ToastWrapper.js ├── hoc │ ├── withOptions.js │ ├── withPortal.js │ ├── withStyles.js │ └── withTheme.js ├── index.html ├── lib │ ├── api.js │ └── store.js ├── modules │ ├── modal.js │ ├── options.js │ ├── root.js │ └── toast.js ├── notifications │ ├── components │ │ ├── FootNote.js │ │ ├── List.js │ │ ├── ListItem.js │ │ ├── Text.js │ │ ├── Title.js │ │ └── index.js │ ├── index.js │ └── templates │ │ ├── 3.0.0.js │ │ ├── 3.1.0.js │ │ ├── 3.2.0.js │ │ ├── 3.2.1.js │ │ └── default.js ├── options │ ├── Components.js │ ├── components │ │ ├── AccentsOnly.js │ │ ├── AccentsOnly.styles.js │ │ ├── AlbumAccents.js │ │ ├── Core.js │ │ ├── Core.observables.js │ │ ├── Core.styles.js │ │ ├── Enabled.js │ │ ├── Enabled.styles.js │ │ ├── Favicon.js │ │ ├── LargeTable.js │ │ ├── LargeTable.styles.js │ │ ├── Logo.js │ │ ├── Logo.styles.js │ │ ├── Menus.js │ │ ├── Menus.styles.js │ │ ├── Playlists.js │ │ ├── Playlists.styles.js │ │ ├── Queue.js │ │ ├── Queue.styles.js │ │ ├── Settings.js │ │ ├── Settings.styles.js │ │ ├── SoundSearch.js │ │ ├── StaticSidebars.js │ │ └── StaticSidebars.styles.js │ └── index.js ├── play-midnight.js ├── style │ ├── global.js │ ├── sheets │ │ ├── accents.js │ │ ├── alerts.js │ │ ├── buttons.js │ │ ├── cardGrid.js │ │ ├── cards.js │ │ ├── core.js │ │ ├── forms.js │ │ ├── index.js │ │ ├── loading.js │ │ ├── menus.js │ │ ├── misc.js │ │ ├── nav.js │ │ ├── page.js │ │ ├── player.js │ │ ├── queue.js │ │ └── songTable.js │ └── theme.js └── utils │ ├── array.js │ ├── awaitElement.js │ ├── checkEnv.js │ ├── createStylesheet.js │ ├── getArrayValue.js │ ├── getCssString.js │ ├── injectElement.js │ ├── removeAllElements.js │ └── validation.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /deploy 11 | /build 12 | /dist 13 | deploy.zip 14 | 15 | # misc 16 | /.cache 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Play Midnight - Chrome Extension 2 | 3 | [![Install now.](https://img.shields.io/badge/chrome%20web%20store-download-blue.svg)](https://chrome.google.com/webstore/detail/play-midnight-for-google/ljmjmhjkcgfmfdhgplikncgndbdeckci) 4 | [![Chrome Web Store](https://img.shields.io/chrome-web-store/d/ljmjmhjkcgfmfdhgplikncgndbdeckci.svg)](https://chrome.google.com/webstore/detail/play-midnight-for-google/ljmjmhjkcgfmfdhgplikncgndbdeckci) 5 | [![Chrome Web Store](https://img.shields.io/chrome-web-store/rating/ljmjmhjkcgfmfdhgplikncgndbdeckci.svg)](https://chrome.google.com/webstore/detail/play-midnight-for-google/ljmjmhjkcgfmfdhgplikncgndbdeckci) 6 |   |   7 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/datducky) 8 | 9 | ### A Nighttime Theme for Google Play Music 10 | 11 | Play Midnight is a different take on the standard theme that is used on Google Play Music. As much as I love the original look of Play, the brightness can hurt the eyes after a while. After noticing there wasn't a dark alternative Play Midnight came to be. 12 | 13 | ### Developing 14 | 15 | Play Midnight is currently running using Node.js/Parcel bundler. You'll have to follow these few steps and you should be up and running. 16 | 17 | 1. Clone Repository 18 | 1. Optionally install `yarn` if you don't have it yet. 19 | * `brew install yarn` if you have homebrew, or `npm install -g yarn` 20 | 1. `cd` into the directory and run `yarn` (or `npm install`) 21 | 22 | ##### Core Updates 23 | 24 | 1. To build work on core changes, run `yarn start` (or `npm run start`) and it should process everything. 25 | 1. In your browser, if you visit `localhost:1234` you'll have a little sandbox you can use for testing core related features 26 | 1. This script will recompile automatically so you can just refresh on save. 27 | 28 | ##### Play Music Updates 29 | 30 | 1. To build changes for Play Music, you'll need to run `yarn dev` after you're ready to test it. 31 | 1. In Chrome, go to `chrome://extensions` and toggle the Developer Mode` option (top right) to "On" 32 | 1. Click `Load Unpacked Extension` and load up your main Play Midnight folder (the one containing `manifest.json`) 33 | 1. Make changes at your leisure! Note: You'll have to click refresh (or Ctrl/Cmd + R) to reload the extension on the `chrome:extensions` page if you rebuild. 34 | 35 | ### About 36 | 37 | The Chrome Extension for Play Midnight uses CSS stored inside the `src/style/sheets/` folder. These files have a theme (from `src/style/theme.js`) injected into them where you have access to the users current Background/Accent colors. 38 | 39 | ### License 40 | 41 | The MIT License (MIT) 42 | 43 | Copyright (c) 2016 Chris Tieman 44 | 45 | Permission is hereby granted, free of charge, to any person obtaining a copy 46 | of this software and associated documentation files (the "Software"), to deal 47 | in the Software without restriction, including without limitation the rights 48 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 49 | copies of the Software, and to permit persons to whom the Software is 50 | furnished to do so, subject to the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be included in all 53 | copies or substantial portions of the Software. 54 | 55 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 56 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 57 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 58 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 59 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 60 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 61 | SOFTWARE. 62 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Fix Panel BG on Disable 2 | 3 | - Fix Firefox Issues (Accent Favicon, BG Page) -------------------------------------------------------------------------------- /icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/icon128.png -------------------------------------------------------------------------------- /icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/icon16.png -------------------------------------------------------------------------------- /icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/icon48.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | } 5 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "applications": { 3 | "gecko": { 4 | "id": "firefox@playmidnight.com" 5 | } 6 | }, 7 | 8 | "manifest_version": 2, 9 | "name": "Play Midnight for Google Play Music™", 10 | "short_name": "Play Midnight", 11 | "version": "3.2.1", 12 | 13 | "description": 14 | "A theme created for Google Play Music™ to give your eyes a break with a darker layout and color options.", 15 | 16 | "icons": { 17 | "16": "icon16.png", 18 | "48": "icon48.png", 19 | "128": "icon128.png" 20 | }, 21 | 22 | "permissions": ["storage", "*://play-music.gstatic.com/*"], 23 | 24 | "background": { 25 | "scripts": ["build/background.js"] 26 | }, 27 | 28 | "content_scripts": [ 29 | { 30 | "matches": ["*://play.google.com/music/listen*"], 31 | "run_at": "document_end", 32 | "js": ["build/play-midnight.js"] 33 | } 34 | ], 35 | 36 | "web_accessible_resources": ["build/*"] 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "play-midnight", 3 | "version": "3.2.1", 4 | "private": true, 5 | "dependencies": { 6 | "babel-eslint": "7.2.3", 7 | "babel-plugin-module-resolver": "^3.0.0", 8 | "babel-preset-react-app": "^3.1.0", 9 | "babel-runtime": "^6.26.0", 10 | "chalk": "^2.3.0", 11 | "dotenv": "4.0.0", 12 | "eslint": "4.10.0", 13 | "eslint-config-react-app": "^2.0.1", 14 | "eslint-loader": "1.9.0", 15 | "eslint-plugin-flowtype": "2.39.1", 16 | "eslint-plugin-import": "2.8.0", 17 | "eslint-plugin-jsx-a11y": "5.1.1", 18 | "eslint-plugin-react": "7.4.0", 19 | "fs-extra": "^5.0.0", 20 | "lodash": "^4.17.4", 21 | "node-vibrant": "^3.0.0", 22 | "parcel-bundler": "^1.3.1", 23 | "react": "^16.2.0", 24 | "react-addons-css-transition-group": "^15.6.2", 25 | "react-color": "^2.13.8", 26 | "react-dev-utils": "^4.2.1", 27 | "react-dom": "^16.2.0", 28 | "react-redux": "^5.0.6", 29 | "redux": "^3.7.2", 30 | "redux-actions": "^2.2.1", 31 | "redux-logger": "^3.0.6", 32 | "redux-saga": "^0.16.0", 33 | "reselect": "^3.0.1", 34 | "semver": "^5.5.0", 35 | "styled-components": "^2.4.0", 36 | "stylis": "^3.4.8", 37 | "tinycolor2": "^1.4.1", 38 | "typeface-roboto": "^0.0.45", 39 | "uuid": "^3.1.0" 40 | }, 41 | "scripts": { 42 | "start": "parcel build src/background.js --out-dir dist && parcel --no-hmr src/index.html", 43 | "dev": 44 | "parcel build src/play-midnight.js --no-minify --out-dir build && parcel build src/background.js --no-minify --out-dir build", 45 | "prod": "node ./scripts/production.js" 46 | }, 47 | "babel": { 48 | "plugins": [ 49 | [ 50 | "module-resolver", 51 | { 52 | "root": ["./src"] 53 | } 54 | ], 55 | ["transform-decorators-legacy"] 56 | ], 57 | "presets": ["react-app"] 58 | }, 59 | "eslintConfig": { 60 | "extends": "react-app", 61 | "globals": { 62 | "browser": true, 63 | "chrome": true 64 | }, 65 | "rules": { 66 | "comma-dangle": ["warn", "always-multiline"], 67 | "max-len": ["error", 120] 68 | } 69 | }, 70 | "devDependencies": { 71 | "babel-plugin-transform-decorators-legacy": "^1.3.4" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /scripts/production.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const chalk = require('chalk'); 4 | const { spawn } = require('child_process'); 5 | 6 | const BUILD_DIRECTORY = `deploy`; 7 | 8 | const NORMALIZED_ROOT = path.normalize(`${__dirname}/..`); 9 | const NORMALIZED_DIRECTORY = path.normalize(`${NORMALIZED_ROOT}/${BUILD_DIRECTORY}`); 10 | 11 | const BUILD_COMMAND = `parcel build src/play-midnight.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`; 12 | const BUILD_BG_COMMAND = `parcel build src/background.js --no-cache --out-dir ${NORMALIZED_DIRECTORY}/build`; 13 | 14 | const copyRoot = async fileName => { 15 | try { 16 | console.log(`\n${NORMALIZED_ROOT}/${fileName} -> ${NORMALIZED_DIRECTORY}/${fileName}`); 17 | await fs.copy(`${NORMALIZED_ROOT}/${fileName}`, `${NORMALIZED_DIRECTORY}/${fileName}`); 18 | console.log(chalk.green(`✅ Success!`)); 19 | } catch (e) { 20 | console.log(chalk.red(`❌ ERROR - Failed to copy file '${fileName}'`)); 21 | } 22 | }; 23 | 24 | const exec = (cmd, name = '') => { 25 | const getParts = cmdStr => { 26 | const parts = cmdStr.split(' '); 27 | return { 28 | command: parts[0], 29 | args: parts.slice(1), 30 | }; 31 | }; 32 | 33 | const { command, args } = getParts(cmd); 34 | const BUILD_PROCESS = spawn(command, args, { 35 | cwd: NORMALIZED_ROOT, 36 | stdio: [process.stdin, process.stdout, 'pipe'], 37 | }); 38 | 39 | BUILD_PROCESS.stderr.on('data', data => { 40 | console.log(chalk.red(`❌ ERROR - ${data}`)); 41 | }); 42 | 43 | BUILD_PROCESS.on('close', data => { 44 | console.log(chalk.green(`✅ ${name} Success!`)); 45 | }); 46 | }; 47 | 48 | const build = async () => { 49 | try { 50 | console.log(chalk.cyan('\n🎉 Starting Bundle Task 🎉 ')); 51 | 52 | console.log(chalk.blue(`\nRemoving Deploy Directory '${NORMALIZED_DIRECTORY}'`)); 53 | await fs.remove(NORMALIZED_DIRECTORY); 54 | console.log(chalk.green(`✅ Success!`)); 55 | 56 | console.log(chalk.blue(`\nCreating Deploy Directory '${NORMALIZED_DIRECTORY}'`)); 57 | await fs.ensureDir(NORMALIZED_DIRECTORY); 58 | console.log(chalk.green(`✅ Success!`)); 59 | 60 | console.log(chalk.blue(`\nCopying package.json`)); 61 | await copyRoot('package.json'); 62 | 63 | console.log(chalk.blue(`\nCopying manifest.json`)); 64 | await copyRoot('manifest.json'); 65 | 66 | console.log(chalk.blue(`\nCopying Icons`)); 67 | await copyRoot('icon16.png'); 68 | await copyRoot('icon48.png'); 69 | await copyRoot('icon128.png'); 70 | 71 | console.log(chalk.blue(`\nRunning Build`)); 72 | console.log(chalk.blue(BUILD_COMMAND)); 73 | exec(BUILD_COMMAND, 'Build'); 74 | 75 | console.log(chalk.blue(`\nRunning Background Build`)); 76 | console.log(chalk.blue(BUILD_BG_COMMAND)); 77 | exec(BUILD_BG_COMMAND, 'Background Build'); 78 | } catch (err) { 79 | console.log(chalk.red(`❌ Build Failed!`)); 80 | console.log(chalk.red(`❌ ERROR - ${err}`)); 81 | } 82 | }; 83 | 84 | build(); 85 | -------------------------------------------------------------------------------- /src/assets/images/Logo.svg: -------------------------------------------------------------------------------- 1 | Asset 1 -------------------------------------------------------------------------------- /src/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/favicon.png -------------------------------------------------------------------------------- /src/assets/images/icon-dropper.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/icon-pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/icon-thumbs-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/icon-thumbs-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/icon-trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/play_music_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/play_music_logo_dark.png -------------------------------------------------------------------------------- /src/assets/images/play_music_logo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/play_music_logo_light.png -------------------------------------------------------------------------------- /src/assets/images/sprites/ani_equalizer_white.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/sprites/ani_equalizer_white.gif -------------------------------------------------------------------------------- /src/assets/images/sprites/ani_loading_white.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/sprites/ani_loading_white.gif -------------------------------------------------------------------------------- /src/assets/images/sprites/equalizer_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducky/play-midnight/3c421b6150c4e476f0ae8fc0ca7ec4b31e9704b2/src/assets/images/sprites/equalizer_white.png -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | const getIcon = ({ accent, url }, sender, cb) => { 2 | const img = new Image(); 3 | 4 | img.onload = function() { 5 | const canvas = document.createElement('canvas'); 6 | 7 | canvas.width = this.naturalWidth; 8 | canvas.height = this.naturalHeight; 9 | canvas.setAttribute('crossOrigin', 'anonymous'); 10 | 11 | const context = canvas.getContext('2d'); 12 | 13 | // Create Colored Icon 14 | context.drawImage(this, 0, 0); 15 | context.globalCompositeOperation = 'source-in'; 16 | context.fillStyle = accent; 17 | context.fillRect(0, 0, this.naturalWidth, this.naturalHeight); 18 | context.fill(); 19 | 20 | const icon = canvas.toDataURL(); 21 | 22 | console.log(icon); 23 | 24 | cb({ url: icon }); 25 | }; 26 | 27 | img.src = url; 28 | 29 | return true; 30 | }; 31 | 32 | chrome.runtime.onMessage.addListener(getIcon); 33 | -------------------------------------------------------------------------------- /src/components/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import withTheme from 'hoc/withTheme'; 5 | 6 | import { isLight, FONT_LIGHT, FONT_DARK, TRANSITION_FAST } from 'style/theme'; 7 | 8 | const StyledButton = styled.button` 9 | display: inline-flex; 10 | text-align: center; 11 | justify-content: center; 12 | align-items: center; 13 | text-transform: uppercase; 14 | border-radius: 3px; 15 | font-weight: 500; 16 | padding: 10px 15px; 17 | cursor: pointer; 18 | opacity: 0.9; 19 | border: none; 20 | outline: none; 21 | box-shadow: none; 22 | background: ${props => props.theme.B25}; 23 | color: ${props => props.theme.font_primary}; 24 | transition: background ${TRANSITION_FAST}, opacity ${TRANSITION_FAST}; 25 | 26 | ${props => props.useAccent && `background: ${props.theme.A500}`}; 27 | ${props => props.useAccent && `color: ${isLight(props.theme.A500) ? FONT_LIGHT : FONT_DARK}`}; 28 | 29 | ${props => props.accent && `background: ${props.accent}`}; 30 | ${props => props.accent && `color: ${isLight(props.accent) ? FONT_LIGHT : FONT_DARK}`}; 31 | 32 | &:not([disabled]):hover { 33 | opacity: 1; 34 | } 35 | 36 | &[disabled] { 37 | opacity: 0.7; 38 | cursor: not-allowed; 39 | } 40 | `; 41 | 42 | const Button = ({ accent, background, theme, noAccent, children, ...rest }) => ( 43 | 44 | {children} 45 | 46 | ); 47 | 48 | export default withTheme(Button); 49 | -------------------------------------------------------------------------------- /src/components/Checkbox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import noop from 'lodash/noop'; 4 | 5 | import withTheme from 'hoc/withTheme'; 6 | import { darken, lighten, TRANSITION_FAST } from 'style/theme'; 7 | 8 | const StyledCheckbox = styled.div` 9 | display: inline-flex; 10 | 11 | .Checkbox__container { 12 | position: relative; 13 | width: 36px; 14 | height: 20px; 15 | cursor: pointer; 16 | } 17 | 18 | .Checkbox__track { 19 | position: absolute; 20 | top: 3px; 21 | left: 0; 22 | right: 0; 23 | bottom: 3px; 24 | background: ${props => props.theme.B500}; 25 | border: 1px solid ${props => props.theme.B500}; 26 | border-radius: 25px; 27 | transition: background ${TRANSITION_FAST}, border-color ${TRANSITION_FAST}, opacity ${TRANSITION_FAST}; 28 | 29 | ${props => props.background && `background: ${darken(props.background, 3)}`}; 30 | ${props => props.background && `border-color: ${darken(props.background, 3)}`}; 31 | } 32 | 33 | .Checkbox__knob { 34 | position: absolute; 35 | left: -2px; 36 | width: 20px; 37 | height: 20px; 38 | background: ${props => props.theme.B200}; 39 | border-radius: 50%; 40 | box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.6); 41 | transform: translateX(0); 42 | transition: transform ${TRANSITION_FAST}, background ${TRANSITION_FAST}; 43 | 44 | ${props => props.background && `background: ${lighten(props.background, 7)}`}; 45 | } 46 | 47 | input:checked + .Checkbox__container .Checkbox__track { 48 | ${props => `background: ${props.theme.A500}`}; 49 | ${props => `border-color: ${props.theme.A600}`}; 50 | ${props => props.accent && `background: ${props.accent}`}; 51 | ${props => props.accent && `border-color: ${darken(props.accent, 3)}`}; 52 | opacity: 0.5; 53 | } 54 | 55 | input:checked + .Checkbox__container .Checkbox__knob { 56 | ${props => `background: ${props.theme.A500}`}; 57 | ${props => `border-color: ${props.theme.A500}`}; 58 | ${props => props.accent && `background: ${props.accent}`}; 59 | ${props => props.accent && `border-color: ${props.accent}`}; 60 | transform: translateX(100%); 61 | } 62 | 63 | input:disabled + .Checkbox__container .Checkbox__track { 64 | cursor: not-allowed; 65 | } 66 | 67 | input:disabled + .Checkbox__container .Checkbox__knob { 68 | cursor: not-allowed; 69 | } 70 | 71 | input { 72 | display: none; 73 | } 74 | `; 75 | 76 | const Checkbox = ({ 77 | accent, 78 | background, 79 | dispatch, 80 | style, 81 | theme, 82 | checked, 83 | disabled, 84 | defaultChecked, 85 | onChange, 86 | ...rest 87 | }) => ( 88 | 89 |