Skip to content

Commit

Permalink
feat: nonce prop (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Jul 14, 2024
1 parent aa1759c commit 5c49bbc
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-rules-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"mode-watcher": patch
---

feat: Add `nonce` prop
17 changes: 11 additions & 6 deletions packages/mode-watcher/src/lib/mode-watcher.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
export let disableTransitions = true;
export let darkClassNames: string[] = ['dark'];
export let lightClassNames: string[] = [];
export let nonce: string = '';
themeColorsStore.set(themeColors);
disableTransitionsStore.set(disableTransitions);
Expand All @@ -51,6 +52,8 @@
const args = `"${defaultMode}"${
themeColors ? `, ${JSON.stringify(themeColors)}` : ', undefined'
}, ${JSON.stringify(darkClassNames)}, ${JSON.stringify(lightClassNames)}`;
$: trueNonce = typeof window === 'undefined' ? nonce : '';
</script>

<svelte:head>
Expand All @@ -60,10 +63,12 @@
<!-- but that snippet does not run in vitest -->
<meta name="theme-color" content={themeColors.dark} />
{/if}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html `<script nonce="%sveltekit.nonce%">(` +
setInitialMode.toString() +
`)(` +
args +
`);</script>`}

{#if trueNonce}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html `<script nonce=${trueNonce}>(` + setInitialMode.toString() + `)(` + args + `);</script>`}
{:else}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html `<script>(` + setInitialMode.toString() + `)(` + args + `);</script>`}
{/if}
</svelte:head>
11 changes: 4 additions & 7 deletions packages/mode-watcher/src/lib/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
themeColors,
disableTransitions,
} from './stores.js';
import { sanitizeClassNames } from './utils.js';
import type { Mode, ThemeColors } from './types.js';

/** Toggle between light and dark mode */
Expand Down Expand Up @@ -37,14 +36,12 @@ export function setInitialMode(
const light =
mode === 'light' ||
(mode === 'system' && window.matchMedia('(prefers-color-scheme: light)').matches);
const sanitizedDarkClassNames = sanitizeClassNames(darkClassNames);
const sanitizedLightClassNames = sanitizeClassNames(lightClassNames);
if (light) {
if (sanitizedDarkClassNames.length) rootEl.classList.remove(...sanitizedDarkClassNames);
if (sanitizedLightClassNames.length) rootEl.classList.add(...sanitizedLightClassNames);
if (darkClassNames.length) rootEl.classList.remove(...darkClassNames);
if (lightClassNames.length) rootEl.classList.add(...lightClassNames);
} else {
if (sanitizedLightClassNames.length) rootEl.classList.remove(...sanitizedLightClassNames);
if (sanitizedDarkClassNames.length) rootEl.classList.add(...sanitizedDarkClassNames);
if (lightClassNames.length) rootEl.classList.remove(...lightClassNames);
if (darkClassNames.length) rootEl.classList.add(...darkClassNames);
}
rootEl.style.colorScheme = light ? 'light' : 'dark';

Expand Down
8 changes: 8 additions & 0 deletions packages/mode-watcher/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,12 @@ export type ModeWatcherProps = {
* @defaultValue `[]`
*/
lightClassNames?: string[];

/**
* An optional nonce to use for the injected script tag to allow-list mode-watcher
* if you are using a Content Security Policy.
*
* @defaultValue `undefined`
*/
nonce?: string;
};
8 changes: 8 additions & 0 deletions sites/docs/content/api-reference/mode-watcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ By default, `ModeWatcher` will add the `dark` class to the root `html` element w

Now, when the mode is dark, the root `html` element will have the `dddd` class, and when the mode is light, the root `html` element will have the `fff` class.

### Nonce

You can use the `nonce` prop to allow-list mode-watcher if you are using a Content Security Policy. This will be applied to the `<script>` tag responsible for setting the initial mode before a FOUC occurs.

```svelte
<ModeWatcher nonce="my-secure-nonce" />
```

## Props

The `ModeWatcher` component accepts the following props:
Expand Down

0 comments on commit 5c49bbc

Please sign in to comment.