├── .eslintrc.js
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── cypress.json
├── demo
├── .browserslistrc
├── .gitignore
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│ ├── favicon-dark.svg
│ ├── favicon.svg
│ └── index.html
└── src
│ ├── App.vue
│ └── main.js
├── docs
├── .vuepress
│ ├── config.js
│ ├── public
│ │ ├── favicon-dark.svg
│ │ └── favicon.svg
│ └── styles
│ │ └── palette.styl
├── README.md
├── guide
│ ├── README.md
│ ├── accessibility.md
│ ├── class-naming.md
│ ├── events.md
│ ├── examples.md
│ ├── favicon.md
│ ├── meta-theme-color.md
│ ├── modes.md
│ └── storage.md
└── howto
│ ├── README.md
│ ├── nuxt.md
│ ├── ssr.md
│ ├── tailwind.md
│ └── vuepress.md
├── index.d.ts
├── jest.config.js
├── package-lock.json
├── package.json
├── rollup.config.dev.js
├── rollup.config.prod.js
├── src
├── DarkMode.vue
├── icons
│ ├── dark.svg
│ ├── light.svg
│ ├── read.svg
│ ├── sepia.svg
│ └── system.svg
├── index.js
├── plugin.js
└── utils.js
└── tests
└── e2e
├── integration
├── aria.spec.js
├── storage.spec.js
└── toggle-class.spec.js
└── support
├── commands.js
└── index.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | "cypress/globals": true,
5 | browser: true,
6 | node: true
7 | },
8 | plugins: [
9 | 'cypress',
10 | 'vuejs-accessibility'
11 | ],
12 | extends: [
13 | 'plugin:vue/recommended',
14 | '@vue/standard',
15 | 'plugin:vuejs-accessibility/recommended'
16 | ],
17 | parserOptions: {
18 | parser: 'babel-eslint'
19 | },
20 | rules: {
21 | 'no-console': 'off'
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .temp
4 | demo/vue-dark-mode.js
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [1.1.1](https://github.com/vue-a11y/vue-dark-mode/compare/v1.1.0...v1.1.1) (2020-07-23)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * Removing title to avoid redundancy on screen readers ([8878fbd](https://github.com/vue-a11y/vue-dark-mode/commit/8878fbd3fe3a6bc5e85a062900a5aa3177690b09))
11 |
12 | ## [1.1.0](https://github.com/vue-a11y/vue-dark-mode/compare/v1.0.1...v1.1.0) (2020-06-23)
13 |
14 |
15 | ### Features
16 |
17 | * Modify color mode when the defaultMode prop changes ([5604170](https://github.com/vue-a11y/vue-dark-mode/commit/560417083ec95adca1ba0b36eb3b1ab3bd31555e))
18 |
19 | ### [1.0.1](https://github.com/vue-a11y/vue-dark-mode/compare/v1.0.0...v1.0.1) (2020-06-11)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * Use selector in favicon prop ([68626a7](https://github.com/vue-a11y/vue-dark-mode/commit/68626a7cd71142f90c9c94f6fe1f577430192580))
25 |
26 | ## [1.0.0](https://github.com/vue-a11y/vue-dark-mode/compare/v0.2.0...v1.0.0) (2020-06-10)
27 |
28 |
29 | ### Features
30 |
31 | * 'change-mode' event when chosen a new color mode ([8fa7193](https://github.com/vue-a11y/vue-dark-mode/commit/8fa7193238e493f4ec66358344f252c3ca05d89b))
32 | * Adds SSR support ([18a1e61](https://github.com/vue-a11y/vue-dark-mode/commit/18a1e61a7af604c61de7813ce66354cdbcb27276))
33 | * Allow to import the component individually ([526d0f9](https://github.com/vue-a11y/vue-dark-mode/commit/526d0f9131f862157ab74e95704d586d9dffefb3))
34 | * Support to custom storage ([4aaf1fb](https://github.com/vue-a11y/vue-dark-mode/commit/4aaf1fbcafeacc4ed26b73ccbb2137f8ca51cd9d))
35 | * Toggle favicon by prefer-color-scheme ([987a555](https://github.com/vue-a11y/vue-dark-mode/commit/987a555ea23c3be696ad7b884b461e42c5239701))
36 |
37 |
38 | ### Bug Fixes
39 |
40 | * If system set colorMode preference in storage to use in SSR ([22e99cc](https://github.com/vue-a11y/vue-dark-mode/commit/22e99cc0fb6a0bd6c0bd2a0d68f2d6cb3ed6331e))
41 | * Set type in package.json ([9fb4aa7](https://github.com/vue-a11y/vue-dark-mode/commit/9fb4aa79d8031da6b636c13b291f2506e3549283))
42 | * **docs:** Path to how to CDN ([9b24257](https://github.com/vue-a11y/vue-dark-mode/commit/9b2425758dca275684a404df665ff0182abb30b7))
43 | * **styles:** Add webkit and moz prefix to appearance button ([7b02fd1](https://github.com/vue-a11y/vue-dark-mode/commit/7b02fd1ee2af36764a6a5eabb972e870ac6e0caf))
44 |
45 | ## [0.2.0](https://github.com/vue-a11y/vue-dark-mode/compare/v0.1.3...v0.2.0) (2020-05-18)
46 |
47 |
48 | ### Features
49 |
50 | * Custom color-mode className ([30a43b5](https://github.com/vue-a11y/vue-dark-mode/commit/30a43b5f55f509b0f5b13abe54a382078a7a77ce))
51 |
52 | ### [0.1.3](https://github.com/vue-a11y/vue-dark-mode/compare/v0.1.2...v0.1.3) (2020-05-08)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * Update rollup plugins and build for production ([d540257](https://github.com/vue-a11y/vue-dark-mode/commit/d5402571432c216b5882c29e6ca48f363b693b1c))
58 |
59 | ### 0.1.2 (2020-05-08)
60 |
61 |
62 | ### Features
63 |
64 | * customize aria-label and aria-live through props ([706b823](https://github.com/vue-a11y/vue-dark-mode/commit/706b823bbde91014bd4b4fb67de9accc776fbfaf))
65 | * index.d.ts with definitions to install function ([4bcc6b7](https://github.com/vue-a11y/vue-dark-mode/commit/4bcc6b7158bb244f5ac9d0e9b49a8a9ceeeab741))
66 |
67 |
68 | ### Bug Fixes
69 |
70 | * import terser module ([9bd904d](https://github.com/vue-a11y/vue-dark-mode/commit/9bd904d595b5596d4b92804d47bbf8109652e4bf))
71 | * remove export component in index.js ([7380c6e](https://github.com/vue-a11y/vue-dark-mode/commit/7380c6ebc52a58a7a9b472745a1ef1f55884dc87))
72 | * Toggle class in listener prefer-color-scheme: dark ([5177f4b](https://github.com/vue-a11y/vue-dark-mode/commit/5177f4b81b678c4e98827b284c0b0b0a3124f6fc))
73 | * Update color mode on the listener only if chosenMode is system ([d3b4153](https://github.com/vue-a11y/vue-dark-mode/commit/d3b4153afbd54e7939fe6d28bd47b2b0d172c2a7))
74 |
75 | ### 0.1.1 (2020-05-08)
76 |
77 |
78 | ### Features
79 |
80 | * customize aria-label and aria-live through props ([706b823](https://github.com/vue-a11y/vue-dark-mode/commit/706b823bbde91014bd4b4fb67de9accc776fbfaf))
81 |
82 |
83 | ### Bug Fixes
84 |
85 | * remove export component in index.js ([7380c6e](https://github.com/vue-a11y/vue-dark-mode/commit/7380c6ebc52a58a7a9b472745a1ef1f55884dc87))
86 | * Toggle class in listener prefer-color-scheme: dark ([5177f4b](https://github.com/vue-a11y/vue-dark-mode/commit/5177f4b81b678c4e98827b284c0b0b0a3124f6fc))
87 | * Update color mode on the listener only if chosenMode is system ([d3b4153](https://github.com/vue-a11y/vue-dark-mode/commit/d3b4153afbd54e7939fe6d28bd47b2b0d172c2a7))
88 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Vue A11Y
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [@vue-a11y/dark-mode](https://vue-dark-mode.surge.sh)
2 | A component that helps to implement dark mode and other color modes in your Vue application.
3 |
4 | # Introduction
5 | Give users more options and control over which color mode they feel most comfortable with.
6 |
7 | Dark mode or any other color mode are excellent alternatives for the user of your application to have the best experience in certain circumstances or by default.
8 |
9 | [@vue-a11y/dark-mode](https://github.com/vue-a11y/vue-dark-mode) is very flexible, in addition to dark-mode, you can pass through props any color mode you want, for example, sepia, dark-blue, reading, and as many more options.
10 |
11 | ### Articles that served as inspiration:
12 | - [Building dark mode on Stack Overflow](https://stackoverflow.blog/2020/03/31/building-dark-mode-on-stack-overflow/)
13 | - [Your dark mode toggle is broken](https://kilianvalkhof.com/2020/design/your-dark-mode-toggle-is-broken/)
14 |
15 | ## Links
16 |
17 | - [Documentation](https://darkmode.vue-a11y.com)
18 | - [Demo](https://vue-dark-mode.surge.sh)
19 |
20 | ## Contributing
21 | - From typos in documentation to coding new features;
22 | - Check the open issues or open a new issue to start a discussion around your feature idea or the bug you found;
23 | - Fork repository, make changes and send a pull request;
24 |
25 | Follow us on Twitter [@vue_a11y](https://twitter.com/vue_a11y)
26 |
27 | **Thank you**
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:8080",
3 | "fixturesFolder": "tests/e2e/fixtures",
4 | "screenshotsFolder": "tests/e2e/screenshots",
5 | "integrationFolder": "tests/e2e/integration",
6 | "fileServerFolder": "demo",
7 | "pluginsFile": false,
8 | "supportFile": "tests/e2e/support"
9 | }
10 |
--------------------------------------------------------------------------------
/demo/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | pnpm-debug.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | # demo
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Customize configuration
19 | See [Configuration Reference](https://cli.vuejs.org/config/).
20 |
--------------------------------------------------------------------------------
/demo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build"
8 | },
9 | "dependencies": {
10 | "core-js": "^3.6.5",
11 | "vue": "^2.6.11"
12 | },
13 | "devDependencies": {
14 | "@vue/cli-plugin-babel": "~4.4.0",
15 | "@vue/cli-service": "~4.4.0",
16 | "vue-template-compiler": "^2.6.11"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/demo/public/favicon-dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | <%= htmlWebpackPlugin.options.title %>
10 |
11 |
12 |
13 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/demo/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
121 |
122 |
123 |
124 | Lorem ipsum dolor sit amet, consectetur adipiscing elit . Aenean finibus iaculis ante non euismod. Integer congue eu eros sed ullamcorper. Integer in est at lorem hendrerit luctus sed luctus purus. Cras eget velit vitae purus hendrerit ultricies. Nunc ut bibendum mi. Praesent semper tortor at odio dignissim, ut vehicula ex mollis. Praesent nibh diam, hendrerit ac erat et, dignissim scelerisque neque. Aenean condimentum pharetra feugiat. Aliquam ex tortor, luctus ut scelerisque in, cursus elementum tellus. Curabitur convallis sit amet sem imperdiet pulvinar. Quisque finibus sem pellentesque neque placerat, sed laoreet urna ornare. Morbi in ex nisl. Nam et erat vel mi volutpat malesuada.
125 |
126 |
127 |
131 |
132 |
133 | Sed neque nunc, gravida vel odio ut, elementum elementum libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis feugiat neque quis nisl tempus, vel varius diam euismod. Duis condimentum placerat ligula, ac scelerisque velit. Nullam mollis massa vitae dolor posuere fermentum. Fusce hendrerit at ex eget commodo. Mauris leo tortor, varius non ipsum sed, commodo lobortis metus. Vivamus pellentesque diam in massa vulputate interdum. Aenean consequat, tellus at fringilla laoreet, nisl augue sagittis risus, mollis pulvinar quam ipsum sit amet neque. Pellentesque laoreet ut lacus ac varius.
134 |
135 |
136 |
137 |
138 |
139 |
140 |
163 |
164 |
236 |
--------------------------------------------------------------------------------
/demo/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | Vue.config.productionTip = false
5 |
6 | new Vue({
7 | render: h => h(App)
8 | }).$mount('#app')
9 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | theme: 'vuepress-theme-default-vue-a11y',
3 | title: 'Vue dark-mode',
4 | description: 'A component that helps to implement dark mode and other color modes in your Vue application.',
5 | serviceWorker: true,
6 | head: [
7 | ['meta', { name: 'theme-color', content: '#fff' }],
8 | ['link', { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }]
9 | ],
10 | themeConfig: {
11 | home: false,
12 | repo: 'vue-a11y/vue-dark-mode',
13 | docsDir: 'docs',
14 | docsBranch: 'master',
15 | editLinks: true,
16 | colorMode: {
17 | props: {
18 | modes: ['light', 'dark', 'system', 'sepia']
19 | }
20 | },
21 | locales: {
22 | '/': {
23 | editLinkText: 'Edit this page on GitHub',
24 | nav: [
25 | {
26 | text: 'Guide',
27 | link: '/guide/'
28 | },
29 | {
30 | text: 'How to',
31 | link: '/howto/'
32 | },
33 | {
34 | text: 'Vue A11y',
35 | ariaLabel: 'More about Vue accessibility project',
36 | items: [
37 | {
38 | text: 'Packages',
39 | items: [
40 | {
41 | text: 'vue-axe',
42 | link: 'https://github.com/vue-a11y/vue-axe'
43 | },
44 | {
45 | text: 'vue-skip-to',
46 | link: 'https://github.com/vue-a11y/vue-skip-to'
47 | },
48 | {
49 | text: 'vue-dark-mode',
50 | link: 'https://github.com/vue-a11y/vue-dark-mode'
51 | },
52 | {
53 | text: 'vue-announcer',
54 | link: 'https://github.com/vue-a11y/vue-announcer'
55 | },
56 | {
57 | text: 'vue-focus-loop',
58 | link: 'https://github.com/vue-a11y/vue-focus-loop'
59 | },
60 | {
61 | text: 'vue-accessible-multiselect',
62 | link: 'https://github.com/vue-a11y/vue-accessible-multiselect'
63 | },
64 | {
65 | text: 'eslint-plugin-vuejs-accessibility',
66 | link: 'https://github.com/vue-a11y/vue-axehttps://github.com/vue-a11y/eslint-plugin-vuejs-accessibility'
67 | },
68 | ]
69 | },
70 | {
71 | text: 'Links',
72 | items: [
73 | {
74 | text: 'Github',
75 | link: 'https://github.com/vue-a11y'
76 | },
77 | {
78 | text: 'Twitter',
79 | link: 'https://twitter.com/vue_a11y'
80 | }
81 | ]
82 | }
83 | ]
84 | }
85 | ],
86 | sidebar: [
87 | '/',
88 | {
89 | title: 'Guide',
90 | collapsable: false,
91 | children: [
92 | '/guide/',
93 | '/guide/class-naming.md',
94 | '/guide/modes.md',
95 | '/guide/events.md',
96 | '/guide/storage.md',
97 | '/guide/favicon.md',
98 | '/guide/meta-theme-color.md',
99 | '/guide/accessibility.md',
100 | '/guide/examples.md'
101 | ]
102 | },
103 | {
104 | title: 'How to',
105 | collapsable: false,
106 | children: [
107 | '/howto/',
108 | '/howto/ssr.md',
109 | '/howto/nuxt.md',
110 | '/howto/vuepress.md',
111 | '/howto/tailwind.md'
112 | ]
113 | }
114 | ]
115 | }
116 | }
117 | }
118 | }
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.vuepress/styles/palette.styl:
--------------------------------------------------------------------------------
1 | $contentMaxWidth = 960px
2 |
3 | $bgCode = #eee
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | A component that helps to implement dark mode and other color modes in your Vue application.
3 |
4 | Give users more options and control over which color mode they feel most comfortable with.
5 |
6 | Dark mode or any other color mode are excellent alternatives for the user of your application to have the best experience in certain circumstances or by default.
7 |
8 | [@vue-a11y/dark-mode](https://github.com/vue-a11y/vue-dark-mode) is very flexible, in addition to dark-mode, you can pass through props any color mode you want, for example, sepia, dark-blue, reading, and as many more as you want.
9 |
10 | **Articles that served as inspiration:**
11 |
12 | - [Building dark mode on Stack Overflow](https://stackoverflow.blog/2020/03/31/building-dark-mode-on-stack-overflow/)
13 | - [Your dark mode toggle is broken](https://kilianvalkhof.com/2020/design/your-dark-mode-toggle-is-broken/)
--------------------------------------------------------------------------------
/docs/guide/README.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | ## Installation
4 | Add `@vue-a11y/dark-mode` in your Vue project.
5 |
6 | ### NPM
7 | ```shell
8 | npm install -S @vue-a11y/dark-mode
9 | ```
10 |
11 | ### Yarn
12 | ```shell
13 | yarn add @vue-a11y/dark-mode
14 | ```
15 |
16 | ## Usage
17 |
18 | ### Globally
19 |
20 | You can use it globally in your main.js
21 |
22 | ```javascript
23 | import Vue from 'vue'
24 | import VueDarkMode from '@vue-a11y/dark-mode'
25 |
26 | Vue.use(VueDarkMode)
27 | ```
28 |
29 | ### Locally
30 |
31 | You can import into your component:
32 |
33 | ```javascript
34 | import { DarkMode } from '@vue-a11y/dark-mode'
35 |
36 | export default {
37 | // ...
38 | components: {
39 | DarkMode
40 | }
41 | }
42 | ```
43 |
44 | ### Single file component
45 |
46 |
47 | ```vue
48 |
49 |
50 |
51 | Color mode: {{ mode }}
52 |
53 |
54 |
55 |
56 |
59 |
60 |
76 | ```
77 |
--------------------------------------------------------------------------------
/docs/guide/accessibility.md:
--------------------------------------------------------------------------------
1 | # Accessibility & i18n
2 |
3 | You can customize the messages that will be announced to screen reader users.
4 | This option is interesting when you have several languages (i18n) in your app.
5 |
6 | ## aria-label
7 |
8 | The aria-label is used to label the `` which is responsible for switching between color modes.
9 |
10 | | Prop | Type | Default
11 | | ------------- | --------- | ----------------------------------------------------
12 | | `ariaLabel` | String | `toggle to %cm mode color`
13 |
14 | ::: tip
15 | **`%cm`**: We use this placeholder to announce the next color mode that will be implemented if the user clicks or presses enter (when focused).
16 | :::
17 |
18 | ## aria-live
19 |
20 | After changing the color mode, the message will be changed in the element with `aria-live="assertive"`, which will immediately announce to the user which color mode has been chosen.
21 |
22 | | Prop | Type | Default
23 | | ------------- | --------- | ----------------------------------------------------
24 | | `ariaLive` | String | `%cm color mode is enabled`
25 |
26 |
27 | ::: tip
28 | **`%cm`**: We use this placeholder to announce the chosen color mode.
29 | :::
30 |
31 |
32 |
33 | ---
34 |
35 | ### Example (spanish)
36 |
37 | ```vue
38 |
42 |
43 | Modo de color: {{ mode }}
44 |
45 |
46 | ```
47 |
48 | **Output:**
49 |
50 | ```html
51 |
55 |
59 | Se eligió el modo de color oscuro.
60 |
61 | ...
62 |
63 | ```
64 |
65 |
--------------------------------------------------------------------------------
/docs/guide/class-naming.md:
--------------------------------------------------------------------------------
1 | # Class naming
2 |
3 | The class by default for deciding the color mode is inserted in the HTML tag. `${color}-mode`
4 |
5 | e.g.
6 |
7 | ```html
8 |
9 |
10 |
11 |
12 | ...
13 | ```
14 |
15 | ## Custom
16 |
17 | You can also customize the class name.
18 |
19 | | prop | Type | default
20 | | -------------- | --------- | ----------------------------------------------------
21 | | `className` | String | `%cm-mode`
22 |
23 | ::: tip
24 | **`%cm`**: We use this placeholder to color mode that will be implemented
25 | :::
26 |
27 | **e.g.**
28 |
29 | ```vue
30 |
31 |
32 | Color mode: {{ mode }}
33 |
34 |
35 | ```
36 |
37 | **Output:**
38 |
39 | ```html
40 |
41 |
42 |
43 |
44 | ...
45 | ```
--------------------------------------------------------------------------------
/docs/guide/events.md:
--------------------------------------------------------------------------------
1 | # Events
2 |
3 | ## change-mode
4 |
5 | Event fired every color mode change.
6 |
7 |
8 | | Event | value |
9 | | -------------- | ------------------------------------------------------------ |
10 | | `change-mode` | color mode token. (`light` \| `dark` \| `system` \| `myCustomMode`) |
11 |
12 | ```vue
13 |
14 |
15 | Color mode: {{ mode }}
16 |
17 |
18 | ```
19 |
20 | ```js
21 | export default {
22 | // ...
23 | methods: {
24 | myColorModeHandler (mode) {
25 | // action here
26 | }
27 | }
28 | }
29 | ```
--------------------------------------------------------------------------------
/docs/guide/examples.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | ## Simple button
4 |
5 | ```vue
6 |
7 |
8 | Color mode: {{ mode }}
9 |
10 |
11 | ```
12 |
13 | **Output**
14 |
15 |
16 |
17 |
18 |
19 | Color mode: {{ mode }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ## Icon Toggle button
30 |
31 | ```vue
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ```
40 |
41 | **Output**
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/docs/guide/favicon.md:
--------------------------------------------------------------------------------
1 | # Favicon
2 |
3 | | prop | Type | default | Description
4 | | ----------- | ----------------------- | --------------------- | ------------------------
5 | | `favicon` | `String` \| `Boolean` | `link[rel="icon"]` | You can define the selector of the desired favicon (e.g. `#favicon`) or to disable this functionality, just by setting it to `false`.
6 |
7 | You can switch the favicon according to the `prefers-color-scheme`.
8 |
9 | ## Naming favicons
10 |
11 | Favicons should be named as follows:
12 |
13 | - For `light` the default favicon will be used, for example: `https://example.com/favicon.svg` or `/favicon.svg`
14 | - For `dark` will be used, for example: `https://example.com/favicon-dark.svg` or `/favicon-dark.svg`
15 |
16 | ::: tip
17 | For `@vue-a11y/dark-mode`:
18 | - You can use absolute URL (`https://example.com/favicon.svg`) or relative (`/favicon.svg`);
19 | - You can use any extension for the favicon. `svg` is currently the most recommended and supported in modern browsers. See more about [svg favicons](https://css-tricks.com/svg-favicons-and-all-the-fun-things-we-can-do-with-them/#why-svg);
20 | :::
21 |
22 | Insert default favicon into your `index.html`
23 |
24 | e.g.
25 |
26 | ```html
27 |
28 |
29 |
30 |
31 |
32 | ```
33 |
34 | If the user has defined in his `dark` operating system as color mode preference for applications, `@vue-a11y/dark-mode` will identify and make the change to your favicon.
35 |
36 | e.g.
37 |
38 | ```html
39 |
40 |
41 |
42 |
43 |
44 | ```
45 |
46 | ---
47 |
48 | ::: tip
49 | You can see this working, just go to your operating system's color settings and change to `dark`, go back to that documentation and check the favicon has changed.
50 |
51 | This is a practice used by several sites, one of which is github.
52 | :::
--------------------------------------------------------------------------------
/docs/guide/meta-theme-color.md:
--------------------------------------------------------------------------------
1 | # Meta theme-color
2 |
3 | "Starting in version 39 of Chrome for Android on Lollipop, you'll now be able to use the theme-color meta tag to set the toolbar color."
4 | -- [Support for theme-color in Chrome 39 for Android](https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android)
5 |
6 | With that in mind, when the user changes the theme, the `@vue-a11y/dark-mode` checks whether you have ` ` and modifies it using the colors defined by the `metaThemeColor` prop, so when the color mode is changed, the toolbar color of Chrome for Android will also be changed.
7 |
8 | | Prop | Type
9 | | ----------------- | ---------
10 | | `metaThemeColor` | Object
11 |
12 | ::: tip
13 | Since `system` assumes light or dark, then you do not need to define theme-color for this mode.
14 | :::
15 |
16 | ```vue
17 |
29 |
30 | Color mode: {{ mode }}
31 |
32 |
33 | ```
--------------------------------------------------------------------------------
/docs/guide/modes.md:
--------------------------------------------------------------------------------
1 | # Color modes
2 |
3 | ## modes
4 |
5 | The `@vue-a11y/dark-mode` component is flexible, you can pass as many color modes as you like. By default we have 3 color modes, which are:
6 |
7 | | prop | Type | default
8 | | ---------- | --------- | ----------------------------------------------------
9 | | `modes` | Array | `[ 'light', 'dark', 'system' ]`
10 |
11 | ### System preference
12 |
13 | This component uses the [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) to automatically detect the color mode chosen by the user in his operating system.
14 |
15 | So, if the user chooses the `system` color mode and is defined in his OS as "dark", then the component will understand and define the theme as dark (`dark-mode`).
16 |
17 | ## defaultMode
18 |
19 | The defaultMode prop will be used as a fallback to define the theme if the following cases happen:
20 |
21 | - The "prefers-color-scheme" was defined as `no-preference`;
22 | - The browser does not support the media query "prefers-color-scheme";
23 | - Happen any case of the items above and also have no color mode stored;
24 |
25 | | Prop | Type | Default
26 | | ------------- | --------- | ----------------------------------------------------
27 | | `defaultMode` | String | `light`
28 |
29 | ### Example of use
30 |
31 | ::: tip
32 | Both props are optional, if not declared in the component, default values will be used.
33 | :::
34 |
35 | ```vue
36 |
45 |
46 | Color mode: {{ mode }}
47 |
48 |
49 | ```
50 |
51 |
--------------------------------------------------------------------------------
/docs/guide/storage.md:
--------------------------------------------------------------------------------
1 | # Storage
2 |
3 | By default the `@vue-a11y/dark-mode` component use **localStorage** to store the user's choice of color mode.
4 |
5 | | Prop | Type | Default
6 | | ------------ | ------------------- | ----------------
7 | | `storage` | String \| Object | `localStorage`
8 |
9 | ```vue
10 |
11 |
12 | Color mode: {{ mode }}
13 |
14 |
15 | ```
16 |
17 | ::: tip
18 | See more about using `cookies` on storage to [support SSR](/howto/ssr.html)
19 | :::
--------------------------------------------------------------------------------
/docs/howto/README.md:
--------------------------------------------------------------------------------
1 | # CDN
2 |
3 | You can use `@vue-a11y/dark-mode` easily through CDN.
4 |
5 | ```html
6 |
7 |
8 | ```
9 |
10 | ::: tip
11 | You do not need to use `Vue.use` because the component is automatically installed globally.
12 | :::
--------------------------------------------------------------------------------
/docs/howto/nuxt.md:
--------------------------------------------------------------------------------
1 | # Nuxt.js
2 |
3 | Nuxt.js already has an incredible color mode module (`@nuxtjs/color-mode`) that you can find here: [https://github.com/nuxt-community/color-mode-module](https://github.com/nuxt-community/color-mode-module)
--------------------------------------------------------------------------------
/docs/howto/ssr.md:
--------------------------------------------------------------------------------
1 | # SSR
2 |
3 | `@vue-a11y/dark-mode` supports SSR applications.
4 |
5 | ::: tip
6 | If you are using `vue-cli` to build your project, we recommend using [@akryum/vue-cli-plugin-ssr](https://github.com/Akryum/vue-cli-plugin-ssr).
7 | ****
8 | :::
9 |
10 | ## Setup
11 |
12 | `@vue-a11y/dark-mode` adds a variable called `colorModeClass` to the SSR context for you to add to your server side template.
13 |
14 | We will use the **@akryum/vue-cli-plugin-ssr** as example.
15 |
16 | In your `public/index.ssr.html`
17 |
18 | ```html{2}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ title }}
27 | {{{ renderResourceHints() }}}
28 | {{{ renderStyles() }}}
29 |
30 |
31 |
32 | {{{ renderState() }}}
33 | {{{ renderState({ contextKey: 'apolloState', windowKey: '__APOLLO_STATE__' }) }}}
34 | {{{ renderScripts() }}}
35 |
36 |
37 | ```
38 |
39 | ## Usage
40 |
41 | For it to work on the server you need to use cookies as storage, just customize the `@vue-a11y/dark-mode` storage api.
42 |
43 | In this example, we use the `universal-cookie` to work in both environments.
44 |
45 | ```vue{2,10,11,15,19,23,24,25,26,27}
46 |
47 |
48 |
49 | Color mode: {{ mode }}
50 |
51 |
52 |
53 |
54 |
76 | ```
--------------------------------------------------------------------------------
/docs/howto/tailwind.md:
--------------------------------------------------------------------------------
1 | # TailwindCSS Dark Mode
2 |
3 | By default, [tailwindcss-dark-mode](https://github.com/ChanceArthur/tailwindcss-dark-mode) uses the `.mode-dark` selector for dark mode.
4 |
5 | It is possible to [changing the Selector](https://github.com/ChanceArthur/tailwindcss-dark-mode#changing-the-selector) in "tailwindcss-dark-mode" plug-in, however, you can also adjust the class using the [className](/guide/class-naming.html) prop to generate the class compatible with the plugin.
6 |
7 | **e.g.**
8 |
9 | ```vue
10 |
11 |
12 | Color mode: {{ mode }}
13 |
14 |
15 | ```
16 |
17 |
18 | **Output**
19 |
20 | ```html
21 |
22 |
23 |
24 |
25 | ...
26 | ```
27 |
--------------------------------------------------------------------------------
/docs/howto/vuepress.md:
--------------------------------------------------------------------------------
1 | # Vuepress
2 |
3 | In Vuepress the use and installation is also simple.
4 |
5 | ::: warning
6 | VuePress applications are rendered by the server in Node.js when generating static builds. To avoid errors, we will wrap it inside the `` component.
7 | :::
8 |
9 | ```vue
10 |
11 |
12 |
13 |
14 | Color mode: {{ mode }}
15 |
16 |
17 |
18 |
19 |
20 |
29 | ```
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { PluginFunction } from 'vue'
2 |
3 | declare class VueDarkMode
4 | {
5 | static install: PluginFunction
6 | }
7 |
8 | export default VueDarkMode
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleNameMapper: {
3 | '^@/(.*)$': '/$1',
4 | '^vue$': 'vue/dist/vue.common.js'
5 | },
6 | moduleFileExtensions: ['js', 'vue', 'json'],
7 | testMatch: [
8 | '**/(*.)test.(js|jsx|ts|tsx)'
9 | ],
10 | transform: {
11 | '^.+\\.js$': 'babel-jest',
12 | '.*\\.(vue)$': 'vue-jest'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vue-a11y/dark-mode",
3 | "version": "1.1.1",
4 | "description": "A component that helps you implement \"dark-mode\" in your Vue app",
5 | "main": "dist/vue-dark-mode.ssr.js",
6 | "browser": "dist/vue-dark-mode.esm.js",
7 | "module": "dist/vue-dark-mode.esm.js",
8 | "unpkg": "dist/vue-dark-mode.min.js",
9 | "types": "index.d.ts",
10 | "scripts": {
11 | "dev": "rollup --config rollup.config.dev.js --watch",
12 | "build": "npm run build:ssr & npm run build:es & npm run build:unpkg",
13 | "build:ssr": "rollup --config rollup.config.prod.js --format cjs --file dist/vue-dark-mode.ssr.js",
14 | "build:es": "rollup --config rollup.config.prod.js --format es --file dist/vue-dark-mode.esm.js",
15 | "build:unpkg": "rollup --config rollup.config.prod.js --format iife --file dist/vue-dark-mode.min.js",
16 | "docs:dev": "vuepress dev docs --no-cache",
17 | "docs:build": "vuepress build docs --no-cache && echo darkmode.vue-a11y.com >> docs/.vuepress/dist/CNAME",
18 | "docs:publish": "gh-pages -d docs/.vuepress/dist",
19 | "demo:dev": "cd demo && npm run serve",
20 | "demo:build": "cd demo && npm run build",
21 | "demo:publish": "cp ./demo/dist/index.html ./demo/dist/200.html && surge ./demo/dist https://vue-dark-mode.surge.sh/",
22 | "release": "standard-version",
23 | "test:unit": "jest",
24 | "test:e2e": "node_modules/.bin/cypress run --headless",
25 | "test:e2e:open": "node_modules/.bin/cypress open ",
26 | "project:publish": "git push --tags origin master && npm run build && npm publish --access public"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/vue-a11y/vue-dark-mode.git"
31 | },
32 | "keywords": [
33 | "vue",
34 | "component",
35 | "a11y",
36 | "accessibility",
37 | "dark-mode",
38 | "light",
39 | "sensitivity",
40 | "visual",
41 | "impairments",
42 | "vue-a11y",
43 | "vuepress",
44 | "vue.js"
45 | ],
46 | "author": "Alan Ktquez (https://ktquez-play.web.app/en/)",
47 | "license": "MIT",
48 | "bugs": {
49 | "url": "https://github.com/vue-a11y/vue-dark-mode/issues"
50 | },
51 | "homepage": "https://github.com/vue-a11y/vue-dark-mode#readme",
52 | "devDependencies": {
53 | "@rollup/plugin-buble": "^0.21.3",
54 | "@rollup/plugin-commonjs": "^11.1.0",
55 | "@rollup/plugin-node-resolve": "^8.0.1",
56 | "@rollup/plugin-replace": "^2.3.3",
57 | "@vue/eslint-config-standard": "^5.1.2",
58 | "@vue/test-utils": "^1.0.3",
59 | "@vuepress/plugin-register-components": "^1.5.0",
60 | "@vuepress/theme-default": "^1.5.0",
61 | "babel-eslint": "^10.1.0",
62 | "babel-jest": "^26.0.1",
63 | "chokidar": "^3.4.0",
64 | "cypress": "^4.8.0",
65 | "eslint": "^6.8.0",
66 | "eslint-plugin-cypress": "^2.11.1",
67 | "eslint-plugin-import": "^2.21.1",
68 | "eslint-plugin-node": "^11.1.0",
69 | "eslint-plugin-promise": "^4.2.1",
70 | "eslint-plugin-standard": "^4.0.1",
71 | "eslint-plugin-vue": "^6.2.2",
72 | "eslint-plugin-vue-a11y": "0.0.31",
73 | "eslint-plugin-vuejs-accessibility": "^0.1.3",
74 | "gh-pages": "^3.0.0",
75 | "jest": "^26.0.1",
76 | "portal-vue": "^2.1.7",
77 | "rollup": "^2.15.0",
78 | "rollup-plugin-eslint": "^7.0.0",
79 | "rollup-plugin-terser": "^6.1.0",
80 | "rollup-plugin-vue": "^5.1.1",
81 | "rollup-plugin-vue-inline-svg": "^1.1.1",
82 | "standard-version": "^8.0.0",
83 | "vue": "^2.6.11",
84 | "vue-template-compiler": "^2.6.11",
85 | "vuepress": "^1.5.3",
86 | "vuepress-theme-default-vue-a11y": "^0.1.15"
87 | },
88 | "dependencies": {}
89 | }
90 |
--------------------------------------------------------------------------------
/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import buble from '@rollup/plugin-buble'
2 | import resolve from '@rollup/plugin-node-resolve'
3 | import chokidar from 'chokidar'
4 | import { eslint } from 'rollup-plugin-eslint'
5 | import vue from 'rollup-plugin-vue'
6 |
7 | export default {
8 | input: 'src/index.js',
9 | watch: {
10 | chokidar,
11 | include: ['src/**']
12 | },
13 | plugins: [
14 | resolve(),
15 | eslint({
16 | include: './src/**'
17 | }),
18 | vue({
19 | css: true,
20 | compileTemplate: true
21 | }),
22 | buble()
23 | ],
24 | output: [
25 | {
26 | name: 'VueDarkMode',
27 | file: 'demo/vue-dark-mode.js',
28 | format: 'umd',
29 | exports: 'named'
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/rollup.config.prod.js:
--------------------------------------------------------------------------------
1 | import buble from '@rollup/plugin-buble'
2 | import commonjs from '@rollup/plugin-commonjs'
3 | import replace from '@rollup/plugin-replace'
4 | import { terser } from 'rollup-plugin-terser'
5 | import vue from 'rollup-plugin-vue'
6 |
7 | export default commandLineArgs => {
8 | return {
9 | input: 'src/index.js',
10 | plugins: [
11 | commonjs(),
12 | replace({
13 | 'process.env.NODE_ENV': JSON.stringify('production')
14 | }),
15 | vue({
16 | css: true,
17 | compileTemplate: true,
18 | template: {
19 | isProduction: true,
20 | optimizeSSR: commandLineArgs.format === 'cjs'
21 | }
22 | }),
23 | buble(),
24 | commandLineArgs.format === 'iife' && terser()
25 | ],
26 | output: {
27 | name: 'VueDarkMode',
28 | exports: 'named'
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/DarkMode.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
14 |
15 |
16 |
206 |
207 |
231 |
--------------------------------------------------------------------------------
/src/icons/dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/read.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/sepia.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/system.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import VueDarkMode from './plugin'
2 |
3 | export default VueDarkMode
4 | export { default as DarkMode } from './DarkMode.vue'
5 |
--------------------------------------------------------------------------------
/src/plugin.js:
--------------------------------------------------------------------------------
1 | import DarkMode from './DarkMode.vue'
2 |
3 | export default function install (Vue) {
4 | if (install.installed) return
5 | install.installed = true
6 | Vue.component('DarkMode', DarkMode)
7 | }
8 |
9 | // auto install
10 | if (typeof window !== 'undefined' && typeof window.Vue !== 'undefined') {
11 | window.Vue.use(install)
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export const storage = storage => {
2 | return {
3 | getItem: key => window[storage].getItem(key),
4 | setItem: (key, value) => window[storage].setItem(key, value)
5 | }
6 | }
7 |
8 | export function getMediaQueryList (type) {
9 | return window.matchMedia(`(prefers-color-scheme: ${type})`)
10 | }
11 |
--------------------------------------------------------------------------------
/tests/e2e/integration/aria.spec.js:
--------------------------------------------------------------------------------
1 | describe('Aria label', () => {
2 | before(() => {
3 | cy.visit('/')
4 | })
5 |
6 | it('Button color mode must be visible', () => {
7 | cy.getColorModeButtonIsVisible()
8 | })
9 |
10 | it('Current color mode must be "light"', () => {
11 | cy.getCurrentColorMode()
12 | .should('contain', 'light')
13 | })
14 |
15 | it('Must have the aria-label attribute and contain text for the "dark" color mode', () => {
16 | cy.getColorModeButton()
17 | .should('have.attr', 'aria-label', 'toggle to dark mode color')
18 | })
19 |
20 | it('Button color mode must be clicked', () => {
21 | cy.toggleColorMode()
22 | })
23 |
24 | it('Current color mode must be "dark"', () => {
25 | cy.getCurrentColorMode()
26 | .should('contain', 'dark')
27 | })
28 |
29 | it('Must have the aria-label attribute and contain text for the "system" color mode', () => {
30 | cy.getColorModeButton()
31 | .should('have.attr', 'aria-label', 'toggle to system mode color')
32 | })
33 | })
34 |
35 | describe('Aria live regions', () => {
36 | before(() => {
37 | cy.visit('/')
38 | })
39 |
40 | it('Button color mode must be visible', () => {
41 | cy.getColorModeButtonIsVisible()
42 | })
43 |
44 | it('Current color mode must be "light"', () => {
45 | cy.getCurrentColorMode()
46 | .should('contain', 'light')
47 | })
48 |
49 | it('There must be an element with aria-live', () => {
50 | cy.getColorModeButton()
51 | .get('span[aria-live]')
52 | })
53 |
54 | it('The element with aria-live must contain "light" in the announced text', () => {
55 | cy.getColorModeButton()
56 | .get('span[aria-live]')
57 | .invoke('text')
58 | .should('eq', 'light color mode is enabled')
59 | })
60 |
61 | it('Button color mode must be clicked', () => {
62 | cy.toggleColorMode()
63 | })
64 |
65 | it('The element with aria-live must contain "dark" in the announced text', () => {
66 | cy.getColorModeButton()
67 | .get('span[aria-live]')
68 | .invoke('text')
69 | .should('eq', 'dark color mode is enabled')
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/tests/e2e/integration/storage.spec.js:
--------------------------------------------------------------------------------
1 | describe('Dark mode storage', () => {
2 | before(() => {
3 | cy.visit('/')
4 | })
5 |
6 | beforeEach(() => {
7 | cy.restoreLocalStorageCache()
8 | })
9 |
10 | afterEach(() => {
11 | cy.saveLocalStorageCache()
12 | })
13 |
14 | it('Button color mode must be visible', () => {
15 | cy.getColorModeButtonIsVisible()
16 | })
17 |
18 | it('Current color mode must be "light"', () => {
19 | cy.getCurrentColorMode().should('contain', 'light')
20 | })
21 |
22 | it('the color mode "light" must contain in localStorage by key "colorMode"', () => {
23 | cy.getLocalStorage('colorMode').should('contain', 'light')
24 | })
25 |
26 | it('Button color mode must be clicked', () => {
27 | cy.toggleColorMode()
28 | })
29 |
30 | it('the color mode "dark" must contain in localStorage by key "colorMode"', () => {
31 | cy.getLocalStorage('colorMode').should('contain', 'dark')
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/tests/e2e/integration/toggle-class.spec.js:
--------------------------------------------------------------------------------
1 | describe('Dark mode toggle class', () => {
2 | before(() => {
3 | cy.visit('/')
4 | })
5 |
6 | it('Button color mode must be visible', () => {
7 | cy.getColorModeButtonIsVisible()
8 | })
9 |
10 | it('Class name must contain "light" color mode', () => {
11 | cy.getColorModeClassName().should('contain', 'light')
12 | })
13 |
14 | it('Current color mode must be "light"', () => {
15 | cy.getCurrentColorMode().should('contain', 'light')
16 | })
17 |
18 | it('Button color mode must be clicked', () => {
19 | cy.toggleColorMode()
20 | })
21 |
22 | it('Class name must contain "dark" color mode', () => {
23 | cy.getColorModeClassName().should('contain', 'dark')
24 | })
25 |
26 | it('Current color mode must be "dark"', () => {
27 | cy.getCurrentColorMode().should('contain', 'dark')
28 | })
29 | })
30 |
--------------------------------------------------------------------------------
/tests/e2e/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
27 | let LOCAL_STORAGE_MEMORY = {}
28 |
29 | Cypress.Commands.add('saveLocalStorageCache', () => {
30 | Object.keys(localStorage).forEach(key => {
31 | LOCAL_STORAGE_MEMORY[key] = localStorage[key]
32 | })
33 | })
34 |
35 | Cypress.Commands.add('restoreLocalStorageCache', () => {
36 | Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
37 | localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key])
38 | })
39 | })
40 |
41 | Cypress.Commands.add('clearLocalStorageCache', () => {
42 | localStorage.clear()
43 | LOCAL_STORAGE_MEMORY = {}
44 | })
45 |
46 | Cypress.Commands.add('getColorModeButton', () => {
47 | cy.get('[data-cy="color-mode-button"')
48 | })
49 |
50 | Cypress.Commands.add('getColorModeButtonIsVisible', () => {
51 | cy.getColorModeButton().should('be.visible')
52 | })
53 |
54 | Cypress.Commands.add('getCurrentColorMode', () => {
55 | cy.getColorModeButton().get('[data-cy="color-mode"]').invoke('text')
56 | })
57 |
58 | Cypress.Commands.add('getColorModeClassName', () => {
59 | cy.document().then((doc) => doc.documentElement.className)
60 | })
61 |
62 | Cypress.Commands.add('getLocalStorage', (key) => {
63 | cy.window().then((window) => window.localStorage.getItem(key))
64 | })
65 |
66 | Cypress.Commands.add('toggleColorMode', () => {
67 | cy.getColorModeButton().click()
68 | })
69 |
70 | Cypress.Commands.add('checkColorModeStorage', (storage, key, token) => {
71 | cy.window()
72 | .its(`${storage}.${key}`)
73 | .should('eq', token)
74 | })
75 |
--------------------------------------------------------------------------------
/tests/e2e/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------