Skip to content

Commit

Permalink
Merge pull request #6 from react18-tools/fix/5-add-nonce
Browse files Browse the repository at this point in the history
Add support for Content Security Policy
  • Loading branch information
mayank1513 authored Jun 10, 2024
2 parents 90c4619 + 110c916 commit 3a5680d
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 7 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,33 @@ The `nextjs-themes` library was initially created to achieve a similar functiona
## Features

- ✅ Simple API to toggle between dark and light modes

- ✅ Perfect dark mode with just 2 lines of code
- ✅ Compatible with Tailwind CSS

- ✅ Compatible with Tailwind CSS, StyledComponents, emotion, Material UI, ...

- ✅ Secure by design - we support `nonce` when you want to apply Content Security Policy

- ✅ Fully treeshakable (e.g., `import from nextjs-darkmode/hooks`)

- ✅ Full TypeScript support

- ✅ Utilizes React 18 Server components

- ✅ Compatible with all React 18 build systems/tools/frameworks

- ✅ System setting with `prefers-color-scheme`

- ✅ Supports Next.js 13 & 14 `appDir`

- ✅ No flash on load (supports SSG, SSR, ISG, and Server Components)

- ✅ Sync theme across tabs and windows

- ✅ Apply custom transitions when changing themes

- ✅ Manipulate theme via the `useMode` hook

- ✅ No cookies when not using the corresponding `ServerTarget`

- ✅ Comprehensive documentation with [Typedoc](https://react18-tools.github.io/nextjs-darkmode)
Expand Down Expand Up @@ -190,6 +205,14 @@ When using `ServerTarget`, use the CSS general sibling combinator (~):

`data-sm` -> System preference

#### Content Security Policy

If you are using CSP rules for CSS files, you can pass `nonce` argument to the `Core` component. If `nonce` is not supplied transition styles will not be applied. This may allow patched transitions throught the page in some cases.

```tsx
<Core nonce={yourNonce} t="transition: all .5s" />
```

### Images

Show different images based on the current theme:
Expand Down
6 changes: 6 additions & 0 deletions lib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# nextjs-darkmode

## 0.1.0

### Minor Changes

- Add nonce to support Content Security Policy

## 0.0.3

### Patch Changes
Expand Down
6 changes: 5 additions & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "nextjs-darkmode",
"author": "Mayank Kumar Chaudhari <https://mayank-chaudhari.vercel.app>",
"private": false,
"version": "0.0.3",
"version": "0.1.0",
"description": "Unleash the Power of React Server Components! Use dark/light mode on your site with confidence, without losing any advantages of React Server Components",
"license": "MPL-2.0",
"main": "./dist/index.js",
Expand Down Expand Up @@ -97,6 +97,10 @@
"Server Components",
"Dark Mode",
"React18 Tools",
"Content Security Policy",
"Security",
"CSP",
"nonce",
"Themes",
"Customizable",
"UI Components",
Expand Down
11 changes: 7 additions & 4 deletions lib/src/client/core/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import { useEffect } from "react";
export interface CoreProps {
/** force apply CSS transition property to all the elements during theme switching. E.g., `all .3s` */
t?: string;
/** The nonce value for your Content Security Policy. */
nonce?: string;
}

/** Modify transition globally to avoid patched transitions */
const modifyTransition = (themeTransition = "none") => {
const modifyTransition = (themeTransition = "none", nonce = "") => {
const css = document.createElement("style");
/** split by ';' to prevent CSS injection */
css.textContent = `*{transition:${themeTransition.split(";")[0]} !important;}`;
nonce && css.setAttribute("nonce", nonce);
document.head.appendChild(css);

return () => {
Expand All @@ -33,7 +36,7 @@ const modifyTransition = (themeTransition = "none") => {
*
* @source - Source code
*/
export const Core = ({ t }: CoreProps) => {
export const Core = ({ t, nonce }: CoreProps) => {
const [{ m: mode, s: systemMode }, setThemeState] = useStore();
const resolvedMode = mode === SYSTEM ? systemMode : mode; // resolvedMode is the actual mode that will be used

Expand All @@ -58,7 +61,7 @@ export const Core = ({ t }: CoreProps) => {
}, []);

useEffect(() => {
const restoreTransitions = modifyTransition(t);
const restoreTransitions = modifyTransition(t, nonce);
const serverTargetEl = document.querySelector("[data-ndm]");
// We need to always update documentElement to support Tailwind configuration
// skipcq: JS-D008, JS-0042 -> map keyword is shorter
Expand All @@ -79,7 +82,7 @@ export const Core = ({ t }: CoreProps) => {
localStorage.setItem(COOKIE_KEY, mode);
if (serverTargetEl)
document.cookie = `${COOKIE_KEY}=${resolvedMode};max-age=31536000;SameSite=Strict;`;
}, [resolvedMode, systemMode, mode, t]);
}, [resolvedMode, systemMode, mode, t, nonce]);

return null;
};
7 changes: 7 additions & 0 deletions packages/shared/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @repo/shared

## 0.0.4

### Patch Changes

- Updated dependencies
- [email protected]

## 0.0.3

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@repo/shared",
"version": "0.0.3",
"version": "0.0.4",
"private": true,
"sideEffects": false,
"main": "./dist/index.js",
Expand Down

0 comments on commit 3a5680d

Please sign in to comment.