svelte-os-themes 2.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -66
- package/dist/ThemeProvider.svelte +1 -5
- package/dist/createTheme.svelte.d.ts +2 -11
- package/dist/createTheme.svelte.js +22 -53
- package/dist/useTheme.svelte.d.ts +4 -2
- package/dist/useTheme.svelte.js +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Svelte OS Themes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Light-weight dark mode helper for [Svelte](https://svelte.dev/).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -24,8 +24,7 @@ npm install svelte-os-themes
|
|
|
24
24
|
storageKey="theme"
|
|
25
25
|
colorScheme={true}
|
|
26
26
|
system={true}
|
|
27
|
-
|
|
28
|
-
scriptNonce=""
|
|
27
|
+
nonce=""
|
|
29
28
|
>
|
|
30
29
|
{@render children()}
|
|
31
30
|
</ThemeProvider>
|
|
@@ -41,13 +40,28 @@ npm install svelte-os-themes
|
|
|
41
40
|
$inspect(theme.current);
|
|
42
41
|
</script>
|
|
43
42
|
|
|
44
|
-
<button
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
onclick={() => {
|
|
46
|
+
theme.current = 'light';
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
45
49
|
Light
|
|
46
50
|
</button>
|
|
47
|
-
<button
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
onclick={() => {
|
|
54
|
+
theme.current = 'dark';
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
48
57
|
Dark
|
|
49
58
|
</button>
|
|
50
|
-
<button
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
onclick={() => {
|
|
62
|
+
theme.current = 'system';
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
51
65
|
System
|
|
52
66
|
</button>
|
|
53
67
|
```
|
|
@@ -93,16 +107,9 @@ npm install svelte-os-themes
|
|
|
93
107
|
accepted values: `true`, `false`<br/>
|
|
94
108
|
default value: `true`
|
|
95
109
|
|
|
96
|
-
- `
|
|
110
|
+
- `nonce`
|
|
97
111
|
|
|
98
|
-
The nonce to use for the injected script.
|
|
99
|
-
|
|
100
|
-
accepted values: `<string>`<br/>
|
|
101
|
-
default value: `undefined`
|
|
102
|
-
|
|
103
|
-
- `styleNonce`
|
|
104
|
-
|
|
105
|
-
The nonce to use for the injected style.
|
|
112
|
+
The nonce to use for the injected script and transition-suppression style.
|
|
106
113
|
|
|
107
114
|
accepted values: `<string>`<br/>
|
|
108
115
|
default value: `undefined`
|
|
@@ -117,57 +124,6 @@ npm install svelte-os-themes
|
|
|
117
124
|
|
|
118
125
|
Returns the current theme when used as a getter and sets the theme when used as a setter.
|
|
119
126
|
|
|
120
|
-
```svelte
|
|
121
|
-
<script>
|
|
122
|
-
import { useTheme } from 'svelte-os-themes';
|
|
123
|
-
|
|
124
|
-
let theme = useTheme();
|
|
125
|
-
</script>
|
|
126
|
-
|
|
127
|
-
<div>Current Theme: {theme.current}</div>
|
|
128
|
-
|
|
129
|
-
<button
|
|
130
|
-
type="button"
|
|
131
|
-
onclick={function () {
|
|
132
|
-
theme.current = 'light';
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
Light
|
|
136
|
-
</button>
|
|
137
|
-
<button
|
|
138
|
-
type="button"
|
|
139
|
-
onclick={function () {
|
|
140
|
-
theme.current = 'dark';
|
|
141
|
-
}}
|
|
142
|
-
>
|
|
143
|
-
Dark
|
|
144
|
-
</button>
|
|
145
|
-
<button
|
|
146
|
-
type="button"
|
|
147
|
-
onclick={function () {
|
|
148
|
-
theme.current = 'system';
|
|
149
|
-
}}
|
|
150
|
-
>
|
|
151
|
-
System
|
|
152
|
-
</button>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- `getTriggerProps`
|
|
156
|
-
|
|
157
|
-
Returns button attributes used to trigger a specific theme.
|
|
158
|
-
|
|
159
|
-
```svelte
|
|
160
|
-
<script>
|
|
161
|
-
import { useTheme } from 'svelte-os-themes';
|
|
162
|
-
|
|
163
|
-
let theme = useTheme();
|
|
164
|
-
</script>
|
|
165
|
-
|
|
166
|
-
<button {...theme.getTriggerProps({value: 'light'})}>Light</button>
|
|
167
|
-
<button {...theme.getTriggerProps({value: 'dark'})}>Dark</button>
|
|
168
|
-
<button {...theme.getTriggerProps({value: 'system'})}>System</button>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
127
|
### parseTheme
|
|
172
128
|
|
|
173
129
|
`parseTheme` is a helper function that parses any value into a valid theme.
|
|
@@ -14,14 +14,10 @@ let {children, ...props}: ThemeProviderProps = $props();
|
|
|
14
14
|
|
|
15
15
|
let theme = createTheme(() => props);
|
|
16
16
|
let script = createTheme.script(() => props);
|
|
17
|
-
let style = createTheme.style(() => props);
|
|
18
17
|
|
|
19
18
|
setThemeContext(theme);
|
|
20
19
|
</script>
|
|
21
20
|
|
|
22
|
-
<svelte:head>
|
|
23
|
-
{@html style.value}
|
|
24
|
-
{@html script.value}
|
|
25
|
-
</svelte:head>
|
|
21
|
+
<svelte:head>{@html script.current}</svelte:head>
|
|
26
22
|
|
|
27
23
|
{@render children?.()}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
2
1
|
import type { Theme } from './types.js';
|
|
3
2
|
export interface CreateThemeOptions {
|
|
4
3
|
/**
|
|
@@ -26,23 +25,15 @@ export interface CreateThemeOptions {
|
|
|
26
25
|
* @default true
|
|
27
26
|
*/
|
|
28
27
|
colorScheme?: boolean;
|
|
29
|
-
|
|
30
|
-
scriptNonce?: string;
|
|
31
|
-
}
|
|
32
|
-
export interface ThemeTriggerProps {
|
|
33
|
-
value: Theme;
|
|
28
|
+
nonce?: string;
|
|
34
29
|
}
|
|
35
30
|
export interface CreateThemeReturn {
|
|
36
31
|
get current(): Theme;
|
|
37
32
|
set current(value: Theme | null | undefined);
|
|
38
|
-
getTriggerProps(props: ThemeTriggerProps): HTMLButtonAttributes;
|
|
39
33
|
}
|
|
40
34
|
export declare function createTheme(options: CreateThemeOptions | (() => CreateThemeOptions)): CreateThemeReturn;
|
|
41
35
|
export declare namespace createTheme {
|
|
42
|
-
var style: (props: CreateThemeOptions | (() => CreateThemeOptions)) => {
|
|
43
|
-
readonly value: string;
|
|
44
|
-
};
|
|
45
36
|
var script: (props: CreateThemeOptions | (() => CreateThemeOptions)) => {
|
|
46
|
-
readonly
|
|
37
|
+
readonly current: string;
|
|
47
38
|
};
|
|
48
39
|
}
|
|
@@ -15,23 +15,18 @@ export function createTheme(options) {
|
|
|
15
15
|
};
|
|
16
16
|
});
|
|
17
17
|
let theme = $state(options_.fallback);
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
type: 'button',
|
|
21
|
-
onclick() {
|
|
22
|
-
theme = props.value;
|
|
23
|
-
},
|
|
24
|
-
'aria-label': 'Enable %s mode'.replace('%s', props.value),
|
|
25
|
-
'data-state': theme === props.value ? 'on' : 'off',
|
|
26
|
-
'data-value': props.value,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
$effect(() => {
|
|
18
|
+
$effect.pre(() => {
|
|
30
19
|
theme = parseTheme(window.localStorage.getItem(options_.storageKey), options_.fallback);
|
|
31
20
|
});
|
|
32
21
|
$effect(() => {
|
|
33
22
|
const html = document.documentElement;
|
|
34
|
-
|
|
23
|
+
const head = document.head;
|
|
24
|
+
const style = document.createElement('style');
|
|
25
|
+
if (options_.nonce) {
|
|
26
|
+
style.setAttribute('nonce', options_.nonce);
|
|
27
|
+
}
|
|
28
|
+
style.appendChild(document.createTextNode(`*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`));
|
|
29
|
+
head.appendChild(style);
|
|
35
30
|
const originalTheme = theme;
|
|
36
31
|
const resolvedTheme = originalTheme === 'system'
|
|
37
32
|
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
@@ -46,11 +41,12 @@ export function createTheme(options) {
|
|
|
46
41
|
else {
|
|
47
42
|
html.setAttribute(options_.attribute, resolvedTheme);
|
|
48
43
|
}
|
|
49
|
-
if (options_.colorScheme)
|
|
44
|
+
if (options_.colorScheme) {
|
|
50
45
|
html.style.colorScheme = resolvedTheme;
|
|
46
|
+
}
|
|
51
47
|
window.localStorage.setItem(options_.storageKey, originalTheme);
|
|
52
48
|
setTimeout(() => {
|
|
53
|
-
|
|
49
|
+
head.removeChild(style);
|
|
54
50
|
}, 1);
|
|
55
51
|
});
|
|
56
52
|
$effect(() => {
|
|
@@ -80,44 +76,16 @@ export function createTheme(options) {
|
|
|
80
76
|
get current() {
|
|
81
77
|
return theme;
|
|
82
78
|
},
|
|
83
|
-
set current(
|
|
84
|
-
if (
|
|
85
|
-
theme =
|
|
79
|
+
set current(value) {
|
|
80
|
+
if (value) {
|
|
81
|
+
theme = value;
|
|
86
82
|
}
|
|
87
83
|
else {
|
|
88
84
|
theme = options_.fallback;
|
|
89
85
|
}
|
|
90
86
|
},
|
|
91
|
-
getTriggerProps,
|
|
92
87
|
};
|
|
93
88
|
}
|
|
94
|
-
createTheme.style = (props) => {
|
|
95
|
-
const options_ = $derived.by(() => {
|
|
96
|
-
const userOptions = typeof props === 'function' ? props() : props;
|
|
97
|
-
return {
|
|
98
|
-
...defaultOptions,
|
|
99
|
-
...userOptions,
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
const value = $derived(`
|
|
103
|
-
<style ${assignNonce(options_.styleNonce)}>
|
|
104
|
-
.svelte-os-themes__no-transition,
|
|
105
|
-
.svelte-os-themes__no-transition *,
|
|
106
|
-
.svelte-os-themes__no-transition *::after,
|
|
107
|
-
.svelte-os-themes__no-transition *::before {
|
|
108
|
-
-webkit-transition: none !important;
|
|
109
|
-
-moz-transition: none !important;
|
|
110
|
-
-o-transition: none !important;
|
|
111
|
-
transition: none !important;
|
|
112
|
-
}
|
|
113
|
-
</style>
|
|
114
|
-
`);
|
|
115
|
-
return {
|
|
116
|
-
get value() {
|
|
117
|
-
return value;
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
89
|
createTheme.script = (props) => {
|
|
122
90
|
const options_ = $derived.by(() => {
|
|
123
91
|
const userOptions = typeof props === 'function' ? props() : props;
|
|
@@ -127,10 +95,9 @@ createTheme.script = (props) => {
|
|
|
127
95
|
};
|
|
128
96
|
});
|
|
129
97
|
const value = $derived(`
|
|
130
|
-
<script ${
|
|
131
|
-
(function(k, a, f, c) {
|
|
98
|
+
<script ${options_.nonce ? `nonce="${options_.nonce}"` : ''}>(function(k, a, f, c) {
|
|
132
99
|
const h = document.documentElement;
|
|
133
|
-
const q = window.matchMedia('(prefers-color-scheme: dark)')
|
|
100
|
+
const q = window.matchMedia('(prefers-color-scheme: dark)');
|
|
134
101
|
const s = window.localStorage.getItem(k)?.toLowerCase().trim();
|
|
135
102
|
|
|
136
103
|
const l = [
|
|
@@ -156,14 +123,16 @@ createTheme.script = (props) => {
|
|
|
156
123
|
'${options_.storageKey}',
|
|
157
124
|
'${options_.attribute}',
|
|
158
125
|
'${options_.fallback}',
|
|
159
|
-
${options_.colorScheme}
|
|
126
|
+
${options_.colorScheme}
|
|
160
127
|
);
|
|
161
128
|
</script>
|
|
162
|
-
`
|
|
129
|
+
`
|
|
130
|
+
.replace(/\n+/g, '')
|
|
131
|
+
.replace(/\s+/g, ' ')
|
|
132
|
+
.trim());
|
|
163
133
|
return {
|
|
164
|
-
get
|
|
134
|
+
get current() {
|
|
165
135
|
return value;
|
|
166
136
|
},
|
|
167
137
|
};
|
|
168
138
|
};
|
|
169
|
-
const assignNonce = (nonce) => (nonce ? `nonce="${nonce}"` : '');
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface UseThemeReturn
|
|
1
|
+
import type { Theme } from './types.js';
|
|
2
|
+
export interface UseThemeReturn {
|
|
3
|
+
get current(): Theme;
|
|
4
|
+
set current(value: Theme | null | undefined);
|
|
3
5
|
}
|
|
4
6
|
export declare const useTheme: () => UseThemeReturn;
|
package/dist/useTheme.svelte.js
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import { getThemeContext } from './ThemeContext.svelte.js';
|
|
2
|
-
export const useTheme = () =>
|
|
2
|
+
export const useTheme = () => {
|
|
3
|
+
const ctx = getThemeContext();
|
|
4
|
+
return {
|
|
5
|
+
get current() {
|
|
6
|
+
return ctx.current;
|
|
7
|
+
},
|
|
8
|
+
set current(value) {
|
|
9
|
+
ctx.current = value;
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
};
|