Skip to content

Commit 18a1e61

Browse files
committed
feat: Adds SSR support
1 parent 4aaf1fb commit 18a1e61

File tree

7 files changed

+126
-35
lines changed

7 files changed

+126
-35
lines changed

Diff for: docs/.vuepress/config.js

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ module.exports = {
5353
collapsable: false,
5454
children: [
5555
'/howto/',
56+
'/howto/ssr.md',
5657
'/howto/nuxt.md',
5758
'/howto/vuepress.md',
5859
'/howto/tailwind.md'

Diff for: docs/guide/storage.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
By default the `@vue-a11y/dark-mode` component use **localStorage** to store the user's choice of color mode.
44

5-
| Prop | Type | Default | Options
6-
| ------------ | --------- | ------------- | -------------------
7-
| `storage` | String | `localStorage` | `localStorage` or `sessionStorage`
5+
| Prop | Type | Default
6+
| ------------ | ------------------- | ----------------
7+
| `storage` | String \| Object | `localStorage`
88

99
```vue
1010
<DarkMode storage="sessionStorage">
@@ -15,5 +15,5 @@ By default the `@vue-a11y/dark-mode` component use **localStorage** to store the
1515
```
1616

1717
::: tip
18-
Currently, the component does not support the use of cookies (essential for use with SSR).
18+
See more about using `cookies` on storage to [support SSR](/howto/ssr.html)
1919
:::

Diff for: docs/howto/ssr.md

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
## Usage
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+
<!DOCTYPE html>
20+
<html lang="en" class="{{{ colorModeClass }}}">
21+
<head>
22+
<meta charset="utf-8">
23+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
24+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
25+
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
26+
<title>{{ title }}</title>
27+
{{{ renderResourceHints() }}}
28+
{{{ renderStyles() }}}
29+
</head>
30+
<body>
31+
<!--vue-ssr-outlet-->
32+
{{{ renderState() }}}
33+
{{{ renderState({ contextKey: 'apolloState', windowKey: '__APOLLO_STATE__' }) }}}
34+
{{{ renderScripts() }}}
35+
</body>
36+
</html>
37+
```
38+
39+
Setup the plugin in `src/main.js`
40+
41+
```js
42+
import Vue from 'vue'
43+
import VueDarkMode from '@vue-a11y/dark-mode'
44+
45+
Vue.use(VueDarkMode)
46+
47+
// ...
48+
```
49+
50+
## Storage
51+
52+
For it to work on the server you need to use cookies as storage, just customize the `@vue-a11y/dark-mode` storage api.
53+
54+
In this example, we use the `universal-cookie` to work in both environments.
55+
56+
```vue{2,10,14,18,19,20,21,22}
57+
<template>
58+
<DarkMode :storage="storage">
59+
<template v-slot="{ mode }">
60+
Color mode: {{ mode }}
61+
</template>
62+
</DarkMode>
63+
</template>
64+
65+
<script>
66+
import Cookies from 'universal-cookie'
67+
68+
export default {
69+
data: () => ({
70+
storage: null
71+
}),
72+
73+
created () {
74+
const cookies = new Cookies(this.$isServer ? this.$ssrContext.req.headers.cookie : null)
75+
this.storage = {
76+
getItem: key => cookies.get(key),
77+
setItem: (key, value) => cookies.set(key, value, { path: '/' })
78+
}
79+
}
80+
}
81+
</script>
82+
```

Diff for: package.json

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@
22
"name": "@vue-a11y/dark-mode",
33
"version": "0.2.0",
44
"description": "A component that helps you implement \"dark-mode\" in your Vue app",
5-
"main": "dist/vue-dark-mode.umd.js",
5+
"main": "dist/vue-dark-mode.ssr.js",
6+
"browser": "dist/vue-dark-mode.esm.js",
67
"module": "dist/vue-dark-mode.esm.js",
78
"unpkg": "dist/vue-dark-mode.min.js",
8-
"browser": {
9-
"./sfc": "src/DarkMode.vue"
10-
},
119
"scripts": {
1210
"dev": "rollup --config rollup.config.dev.js --watch",
13-
"build": "npm run build:umd & npm run build:es & npm run build:unpkg",
14-
"build:umd": "rollup --config rollup.config.prod.js --format umd --file dist/vue-dark-mode.umd.js",
11+
"build": "npm run build:ssr & npm run build:es & npm run build:unpkg",
12+
"build:ssr": "rollup --config rollup.config.prod.js --format cjs --file dist/vue-dark-mode.ssr.js",
1513
"build:es": "rollup --config rollup.config.prod.js --format es --file dist/vue-dark-mode.esm.js",
1614
"build:unpkg": "rollup --config rollup.config.prod.js --format iife --file dist/vue-dark-mode.min.js",
17-
"docs": "vuepress dev docs --no-cache",
15+
"docs:dev": "vuepress dev docs --no-cache",
1816
"docs:build": "vuepress build docs --no-cache && echo darkmode.vue-a11y.com >> docs/.vuepress/dist/CNAME",
1917
"docs:publish": "gh-pages -d docs/.vuepress/dist",
2018
"demo:dev": "cd demo && npm run serve",

Diff for: rollup.config.prod.js

+23-20
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@ import replace from '@rollup/plugin-replace'
44
import { terser } from 'rollup-plugin-terser'
55
import vue from 'rollup-plugin-vue'
66

7-
export default {
8-
input: 'src/index.js',
9-
plugins: [
10-
commonjs(),
11-
replace({
12-
'process.env.NODE_ENV': JSON.stringify('production')
13-
}),
14-
vue({
15-
css: true,
16-
compileTemplate: true,
17-
template: {
18-
isProduction: true
19-
}
20-
}),
21-
buble(),
22-
terser()
23-
],
24-
output: {
25-
name: 'VueDarkMode',
26-
exports: 'named'
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+
}
2730
}
2831
}

Diff for: src/DarkMode.vue

+11-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export default {
4040
},
4141
storage: {
4242
type: [String, Object],
43+
validator: storage => {
44+
if (typeof storage === 'string') return ['localStorage', 'sessionStorage'].includes(storage)
45+
return Object.keys(storage).every(key => ['getItem', 'setItem'].includes(key))
46+
},
4347
default: 'localStorage'
4448
},
4549
metaThemeColor: {
@@ -96,10 +100,13 @@ export default {
96100
},
97101
98102
getStorage () {
103+
if (typeof this.storage !== 'string') return this.storage
104+
if (this.$isServer) return false
99105
return storage(this.storage)
100106
},
101107
102108
getStorageColorMode () {
109+
if (!this.getStorage) return this.defaultMode
103110
return this.getStorage.getItem('colorMode')
104111
},
105112
@@ -132,7 +139,7 @@ export default {
132139
methods: {
133140
setMode (chosenMode) {
134141
this.chosenMode = chosenMode
135-
this.getStorage.setItem('colorMode', this.chosenMode)
142+
if (this.getStorage) this.getStorage.setItem('colorMode', this.chosenMode)
136143
this.handleColorModeClass('add')
137144
if (Object.keys(this.metaThemeColor).length) this.setMetaThemeColor(this.metaThemeColor[this.currentMode] || this.metaThemeColor[this.getPrefersColorScheme])
138145
this.$emit('change-mode', this.chosenMode)
@@ -159,8 +166,9 @@ export default {
159166
},
160167
161168
handleColorModeClass (action) {
162-
if (this.$isServer) return
163-
return document.documentElement.classList[action](`${this.className.replace(/%cm/g, this.currentMode)}`)
169+
const className = `${this.className.replace(/%cm/g, this.currentMode)}`
170+
if (!this.$isServer) return document.documentElement.classList[action](className)
171+
this.$ssrContext.colorModeClass = this.currentMode === 'system' ? '' : className // Adds the className in the ssr context for the user to insert as they wish in the HTML tag
164172
},
165173
166174
handlePreferColorScheme (e) {

Diff for: src/utils.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export const storage = storage => {
2-
if (typeof storage !== 'string') return storage
32
return {
43
getItem: key => window[storage].getItem(key),
54
setItem: (key, value) => window[storage].setItem(key, value)

0 commit comments

Comments
 (0)