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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Svelte OS Themes
2
2
 
3
- Lightweight dark mode helper for [Svelte](https://svelte.dev/).
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
- styleNonce=""
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 {...theme.getTriggerProps({value: 'light'})}>
43
+ <button
44
+ type="button"
45
+ onclick={() => {
46
+ theme.current = 'light';
47
+ }}
48
+ >
45
49
  Light
46
50
  </button>
47
- <button {...theme.getTriggerProps({value: 'dark'})}>
51
+ <button
52
+ type="button"
53
+ onclick={() => {
54
+ theme.current = 'dark';
55
+ }}
56
+ >
48
57
  Dark
49
58
  </button>
50
- <button {...theme.getTriggerProps({value: 'system'})}>
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
- - `scriptNonce`
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
- styleNonce?: string;
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 value: string;
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
- function getTriggerProps(props) {
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
- html.classList.add('svelte-os-themes__no-transition');
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
- html.classList.remove('svelte-os-themes__no-transition');
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(newTheme) {
84
- if (newTheme) {
85
- theme = newTheme;
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 ${assignNonce(options_.scriptNonce)}>
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 value() {
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 { CreateThemeReturn } from './createTheme.svelte.js';
2
- export interface UseThemeReturn extends CreateThemeReturn {
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;
@@ -1,2 +1,12 @@
1
1
  import { getThemeContext } from './ThemeContext.svelte.js';
2
- export const useTheme = () => getThemeContext();
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
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "svelte-os-themes",
3
3
  "type": "module",
4
4
  "license": "MIT",
5
- "version": "2.0.0",
5
+ "version": "4.0.0",
6
6
  "main": "./dist/index.js",
7
7
  "svelte": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",