mirror of
https://github.com/jakejarvis/dark-mode.git
synced 2026-06-05 19:25:28 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
89a853f4f4
|
@@ -1,17 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
# this file is the top-most editorconfig file
|
||||
root = true
|
||||
|
||||
# all files
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# site content
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"@jakejarvis/eslint-config",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"dist/**"
|
||||
]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
* text=auto eol=lf
|
||||
@@ -1,10 +0,0 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
versioning-strategy: increase
|
||||
schedule:
|
||||
interval: "daily"
|
||||
commit-message:
|
||||
prefix: "📦 npm:"
|
||||
@@ -1,19 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn lint
|
||||
- run: yarn build
|
||||
@@ -1,20 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1,5 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
.npmrc
|
||||
.vscode/
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2021 Jake Jarvis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,87 +0,0 @@
|
||||
# 🌓 Dark Mode Switcheroo™
|
||||
|
||||
[](https://github.com/jakejarvis/dark-mode/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/dark-mode-switcheroo)
|
||||
|
||||
Very simple CSS dark/light mode toggler with saved preference via local storage & dynamic OS setting detection. Zero dependencies and [only ~500 bytes gzipped!](https://bundlephobia.com/package/dark-mode-switcheroo)
|
||||
|
||||
- [View the example.](https://jakejarvis.github.io/dark-mode/)
|
||||
- [Read the blog post.](https://jarv.is/notes/dark-mode/)
|
||||
- [See it in action.](https://jarv.is/)
|
||||
|
||||
## Usage
|
||||
|
||||
### Options
|
||||
|
||||
`darkMode.init([...options])`
|
||||
|
||||
- **`toggle`**: The clickable [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) used to toggle between the two themes. (optional, default: `null`)
|
||||
- **`classes`**: An object containing the `<body>` class names for the light and dark themes. (optional, default: `{ light: "light", dark: "dark" }`)
|
||||
- **`default`**: The initial `<body>` class hard-coded into the HTML template. (optional, default: `"light"`)
|
||||
- **`storageKey`**: Name of the `localStorage` key holding the user's preference. (optional, default: `"dark_mode_pref"`)
|
||||
- **`onInit([toggle])`**: Callback function executed at the end of initialization. The toggle above is passed in if set. (optional, default: `null`)
|
||||
- **`onUserToggle([toggle])`**: Callback function executed when a user manually interacts with the toggle button. The toggle above (if set) is passed in. (optional, default: `null`)
|
||||
- **`onChange([theme, toggle])`**: Callback function executed when theme is switched. The new theme and the toggle above (if set) are passed in. (optional, default: `null`)
|
||||
|
||||
### Browser
|
||||
|
||||
```html
|
||||
<button class="dark-mode-toggle" style="visibility: hidden;">💡 Click to see the light... or not.</button>
|
||||
|
||||
<script src="https://unpkg.com/dark-mode-switcheroo/dist/dark-mode.min.js"></script>
|
||||
<script>
|
||||
window.darkMode.init({
|
||||
toggle: document.querySelector(".dark-mode-toggle"),
|
||||
classes: {
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
},
|
||||
default: "light",
|
||||
storageKey: "dark_mode_pref",
|
||||
onInit: function (toggle) {
|
||||
toggle.style.visibility = "visible"; // toggle appears now that we know JS is enabled
|
||||
},
|
||||
onChange: function (theme, toggle) {
|
||||
console.log("Theme is now " + theme);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Node
|
||||
|
||||
```bash
|
||||
npm install dark-mode-switcheroo
|
||||
# or...
|
||||
yarn add dark-mode-switcheroo
|
||||
```
|
||||
|
||||
#### Module via `import`
|
||||
|
||||
```js
|
||||
import { init } from "dark-mode-switcheroo";
|
||||
|
||||
init({
|
||||
// ...same as browser.
|
||||
});
|
||||
```
|
||||
|
||||
#### CommonJS via `require()`
|
||||
|
||||
```js
|
||||
const darkMode = require("dark-mode-switcheroo");
|
||||
|
||||
darkMode.init({
|
||||
// ...same as browser.
|
||||
});
|
||||
```
|
||||
|
||||
## To-Do
|
||||
|
||||
- [ ] Support more than two themes
|
||||
- [ ] Better readme docs
|
||||
- [x] Add callback function `onChange` (or `onToggle` etc.) passed in as an option
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -45,12 +45,18 @@
|
||||
|
||||
<button class="dark-mode-toggle">💡 Click to see the light... or not.</button>
|
||||
|
||||
<p><a href="https://github.com/jakejarvis/dark-mode" target="_blank" rel="noopener">View the source code</a> or <a href="https://jarv.is/notes/dark-mode/" target="_blank" rel="noopener">read the post</a>.</p>
|
||||
<p><a href="https://github.com/jakejarvis/dark-mode" target="_blank" rel="noopener">GitHub Repo</a> · <a href="https://github.com/jakejarvis/dark-mode/blob/gh-pages/index.html" target="_blank" rel="noopener">Source Code</a></p>
|
||||
|
||||
<script src="../dist/dark-mode.min.js"></script> <!-- or use CDN: https://unpkg.com/dark-mode-switcheroo/dist/dark-mode.min.js -->
|
||||
<script src="https://unpkg.com/@jakejarvis/dark-mode/dist/dark-mode.min.js"></script>
|
||||
<script>
|
||||
window.darkMode.init({
|
||||
toggle: document.querySelector(".dark-mode-toggle"),
|
||||
classes: {
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
},
|
||||
default: "light",
|
||||
storageKey: "example_dark_mode_pref",
|
||||
onInit: function (e) {
|
||||
e.style.visibility = "visible";
|
||||
console.log("Toggle is visible now that we know user has JS enabled.");
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"name": "dark-mode-switcheroo",
|
||||
"version": "0.10.0",
|
||||
"description": "🌓 Simple CSS theme switching with saved preferences and automatic OS setting detection",
|
||||
"license": "MIT",
|
||||
"homepage": "https://jrvs.io/darkmode",
|
||||
"author": {
|
||||
"name": "Jake Jarvis",
|
||||
"email": "jake@jarv.is",
|
||||
"url": "https://jarv.is/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jakejarvis/dark-mode.git"
|
||||
},
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"source": "./src/dark-mode.ts",
|
||||
"main": "./dist/dark-mode.cjs",
|
||||
"module": "./dist/dark-mode.esm.js",
|
||||
"unpkg": "./dist/dark-mode.min.js",
|
||||
"exports": {
|
||||
"require": "./dist/dark-mode.cjs",
|
||||
"import": "./dist/dark-mode.esm.js",
|
||||
"browser": "./dist/dark-mode.min.js"
|
||||
},
|
||||
"types": "./dist/dark-mode.d.ts",
|
||||
"scripts": {
|
||||
"build": "microbundle --format cjs,esm,umd --name 'darkMode'",
|
||||
"lint": "eslint .",
|
||||
"prepublishOnly": "yarn build"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@jakejarvis/eslint-config": "*",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.1",
|
||||
"@typescript-eslint/parser": "^5.3.1",
|
||||
"eslint": "^8.2.0",
|
||||
"microbundle": "^0.14.1",
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"keywords": [
|
||||
"front-end",
|
||||
"dark mode",
|
||||
"theme",
|
||||
"appearance",
|
||||
"design",
|
||||
"css"
|
||||
]
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
export function init(options: {
|
||||
toggle?: Element | null;
|
||||
classes?: { dark: string, light: string };
|
||||
default?: string;
|
||||
storageKey?: string;
|
||||
onInit?: (toggle?: Element | null) => unknown;
|
||||
onUserToggle?: (toggle?: Element | null) => unknown;
|
||||
onChange?: (theme: string, toggle?: Element | null) => unknown;
|
||||
}): void {
|
||||
options = options || {};
|
||||
|
||||
// use a specified element(s) to trigger swap when clicked
|
||||
const toggle = options.toggle || null;
|
||||
|
||||
// check for preset `dark_mode_pref` preference in local storage
|
||||
const storageKey = options.storageKey || "dark_mode_pref";
|
||||
const pref = localStorage.getItem(storageKey);
|
||||
|
||||
// change CSS via these <body> classes:
|
||||
const dark = options.classes ? options.classes.dark : "dark";
|
||||
const light = options.classes ? options.classes.light : "light";
|
||||
|
||||
// which class is <body> set to initially?
|
||||
const defaultTheme = options.default || "light";
|
||||
|
||||
// keep track of current state no matter how we got there
|
||||
let active = defaultTheme === dark;
|
||||
|
||||
// receives a class name and switches <body> to it
|
||||
const activateTheme = function (theme: string, remember?: boolean) {
|
||||
// optional onChange callback function passed as option
|
||||
if (typeof options.onChange === "function") {
|
||||
options.onChange(theme, toggle);
|
||||
}
|
||||
|
||||
document.body.classList.remove(dark, light);
|
||||
document.body.classList.add(theme);
|
||||
active = theme === dark;
|
||||
|
||||
if (remember) {
|
||||
localStorage.setItem(storageKey, theme);
|
||||
}
|
||||
};
|
||||
|
||||
// optional onInit callback function passed as option
|
||||
if (typeof options.onInit === "function") {
|
||||
options.onInit(toggle);
|
||||
}
|
||||
|
||||
// user has never clicked the button, so go by their OS preference until/if they do so
|
||||
if (!pref) {
|
||||
// returns media query selector syntax
|
||||
const prefers = function (colorScheme: "dark" | "light") {
|
||||
// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
|
||||
return `(prefers-color-scheme: ${colorScheme})`;
|
||||
};
|
||||
|
||||
// check for OS dark/light mode preference and switch accordingly
|
||||
// default to `defaultTheme` set above if unsupported
|
||||
if (window.matchMedia(prefers("dark")).matches) {
|
||||
activateTheme(dark);
|
||||
} else if (window.matchMedia(prefers("light")).matches) {
|
||||
activateTheme(light);
|
||||
} else {
|
||||
activateTheme(defaultTheme);
|
||||
}
|
||||
|
||||
// real-time switching if supported by OS/browser
|
||||
window.matchMedia(prefers("dark")).addEventListener("change", function (e) {
|
||||
if (e.matches) {
|
||||
activateTheme(dark);
|
||||
}
|
||||
});
|
||||
window.matchMedia(prefers("light")).addEventListener("change", function (e) {
|
||||
if (e.matches) {
|
||||
activateTheme(light);
|
||||
}
|
||||
});
|
||||
} else if (pref === dark || pref === light) {
|
||||
// if user already explicitly toggled in the past, restore their preference
|
||||
activateTheme(pref);
|
||||
} else {
|
||||
// fallback to default theme (this shouldn't happen)
|
||||
activateTheme(defaultTheme);
|
||||
}
|
||||
|
||||
// don't freak out if page happens not to have a toggle
|
||||
if (toggle !== null) {
|
||||
// handle toggle click
|
||||
toggle.addEventListener("click", function () {
|
||||
// optional onUserToggle callback function passed as option
|
||||
if (typeof options.onUserToggle === "function") {
|
||||
options.onUserToggle(toggle);
|
||||
}
|
||||
|
||||
// switch to the opposite theme & save preference in local storage
|
||||
if (active) {
|
||||
activateTheme(light, true);
|
||||
} else {
|
||||
activateTheme(dark, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user