Compare commits

..
1 Commits
Author SHA1 Message Date
jake 89a853f4f4 publish example to GH pages 2021-08-05 12:01:25 -04:00
11 changed files with 8 additions and 3869 deletions
-18
View File
@@ -1,18 +0,0 @@
{
"extends": [
"eslint:recommended",
"plugin:compat/recommended"
],
"env": {
"browser": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"rules": {
"quotes": ["error", "double"],
"semi": ["error", "always"]
}
}
-18
View File
@@ -1,18 +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: '14.x'
- run: yarn install --frozen-lockfile
- run: yarn lint
-5
View File
@@ -1,5 +0,0 @@
.DS_Store
node_modules/
dist/
.npmrc
.vscode/
-19
View File
@@ -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.
-87
View File
@@ -1,87 +0,0 @@
# 🌓 Dark Mode Switcheroo™
[![CI](https://github.com/jakejarvis/dark-mode.js/actions/workflows/ci.yml/badge.svg)](https://github.com/jakejarvis/dark-mode.js/actions/workflows/ci.yml)
[![npm (scoped)](https://img.shields.io/npm/v/@jakejarvis/dark-mode)](https://www.npmjs.com/package/@jakejarvis/dark-mode)
[![MIT License](https://img.shields.io/github/license/jakejarvis/dark-mode?color=violet)](LICENSE)
Very simple CSS dark/light mode toggler with saved preference via local storage & dynamic OS setting detection. Zero dependencies and only ~600 bytes gzipped!
- [View the example.](https://jakejarvis.github.io/dark-mode-example/)
- [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`)
- **`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/@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: "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 @jakejarvis/dark-mode
# or...
yarn add @jakejarvis/dark-mode
```
#### Module via `import`
```js
import { init } from "@jakejarvis/dark-mode";
init({
// ...same as browser.
});
```
#### CommonJS via `require()`
```js
const darkMode = require("@jakejarvis/dark-mode");
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
+8 -2
View File
@@ -45,12 +45,18 @@
<button class="dark-mode-toggle">💡 Click to see the light... or not.</button> <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> &middot; <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/@jakejarvis/dark-mode/dist/dark-mode.min.js --> <script src="https://unpkg.com/@jakejarvis/dark-mode/dist/dark-mode.min.js"></script>
<script> <script>
window.darkMode.init({ window.darkMode.init({
toggle: document.querySelector(".dark-mode-toggle"), toggle: document.querySelector(".dark-mode-toggle"),
classes: {
light: "light",
dark: "dark",
},
default: "light",
storageKey: "example_dark_mode_pref",
onInit: function (e) { onInit: function (e) {
e.style.visibility = "visible"; e.style.visibility = "visible";
console.log("Toggle is visible now that we know user has JS enabled."); console.log("Toggle is visible now that we know user has JS enabled.");
-49
View File
@@ -1,49 +0,0 @@
{
"name": "@jakejarvis/dark-mode",
"version": "0.6.0",
"description": "🌓 Simple CSS theme switching with saved preferences and automatic OS setting detection",
"license": "MIT",
"author": {
"name": "Jake Jarvis",
"email": "jake@jarv.is",
"url": "https://jarv.is/"
},
"repository": {
"type": "git",
"url": "https://github.com/jakejarvis/dark-mode.git"
},
"main": "dist/dark-mode.cjs.js",
"module": "dist/dark-mode.esm.js",
"unpkg": "dist/dark-mode.min.js",
"types": "dist/dark-mode.d.ts",
"scripts": {
"build": "rollup -c",
"lint": "eslint src/**/*.js",
"prepublishOnly": "rm -rf dist && yarn build"
},
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-eslint": "^8.0.1",
"@rollup/plugin-node-resolve": "^13.0.4",
"eslint": "^7.32.0",
"eslint-plugin-compat": "^3.11.1",
"rollup": "^2.56.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-filesize": "^9.1.1",
"rollup-plugin-terser": "^7.0.2"
},
"files": [
"dist"
],
"keywords": [
"frontend",
"dark mode",
"theme",
"appearance",
"design",
"css"
]
}
-87
View File
@@ -1,87 +0,0 @@
import resolve from "@rollup/plugin-node-resolve";
import { babel } from "@rollup/plugin-babel";
import { terser } from "rollup-plugin-terser";
import eslint from "@rollup/plugin-eslint";
import filesize from "rollup-plugin-filesize";
import copy from "rollup-plugin-copy";
const banner = "/*! Dark Mode Switcheroo | MIT License | jrvs.io/darkmode */";
export default [
{
// universal (browser and node)
input: "src/index.js",
output: [
{
name: "darkMode",
file: "dist/dark-mode.js",
format: "umd",
exports: "named",
esModule: false,
banner: banner,
},
{
name: "darkMode",
file: "dist/dark-mode.min.js",
format: "umd",
exports: "named",
esModule: false,
plugins: [
terser({
output: {
preamble: banner,
},
}),
],
},
],
plugins: [
copy({
// clearly this isn't really typescript, so we need to manually copy the type definition file
targets: [
{
src: "src/index.d.ts",
dest: "dist",
rename: "dark-mode.d.ts",
},
],
}),
resolve(),
eslint(),
babel({
babelHelpers: "bundled",
presets: [["@babel/preset-env"]],
exclude: ["node_modules/**"],
}),
filesize(),
],
},
{
// modules
input: "src/index.js",
output: [
{
// ES6 module (import)
file: "dist/dark-mode.esm.js",
format: "esm",
exports: "named",
banner: banner,
},
{
// commonjs (require)
file: "dist/dark-mode.cjs.js",
format: "cjs",
exports: "named",
banner: banner,
},
],
plugins: [
resolve(),
babel({
babelHelpers: "bundled",
exclude: ["node_modules/**"],
}),
filesize(),
],
},
];
-8
View File
@@ -1,8 +0,0 @@
export function init(options?: {
toggle?: HTMLElement;
classes?: { dark: string, light: string };
default?: string;
storageKey?: string;
onInit?: (toggle?: HTMLElement) => void;
onChange?: (theme?: string, toggle?: HTMLElement) => void;
}): void;
-111
View File
@@ -1,111 +0,0 @@
"use strict";
const init = function (options) {
// { toggle, classes: { light, dark }, default, storageKey }
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 storageAvailable = isStorageAvailable();
const pref = storageAvailable ? localStorage.getItem(storageKey) : null;
// 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, remember) {
document.body.classList.remove(dark, light);
document.body.classList.add(theme);
active = theme === dark;
if (storageAvailable && !!remember) {
localStorage.setItem(storageKey, theme);
}
// optional onChange callback function passed as option
if (typeof options.onChange === "function") {
options.onChange(theme, 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) {
// 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 () {
// switch to the opposite theme & save preference in local storage
if (active) {
activateTheme(light, true);
} else {
activateTheme(dark, true);
}
});
}
// optional onInit callback function passed as option
if (typeof options.onInit === "function") {
options.onInit(toggle);
}
};
// recommended method (by MDN) to detect localStorage availability:
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#feature-detecting_localstorage
const isStorageAvailable = function () {
try {
var storage = window["localStorage"];
var x = "__storage_test__";
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return false;
}
};
export { init };
-3465
View File
File diff suppressed because it is too large Load Diff