diff --git a/README.md b/README.md index 21b8a6d9..d5506dfb 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 + +``` + ### Images Show different images based on the current theme: diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index ba81540f..32ada8bc 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # nextjs-darkmode +## 0.1.0 + +### Minor Changes + +- Add nonce to support Content Security Policy + ## 0.0.3 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index 2392b552..9e17a3eb 100644 --- a/lib/package.json +++ b/lib/package.json @@ -2,7 +2,7 @@ "name": "nextjs-darkmode", "author": "Mayank Kumar Chaudhari ", "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", @@ -97,6 +97,10 @@ "Server Components", "Dark Mode", "React18 Tools", + "Content Security Policy", + "Security", + "CSP", + "nonce", "Themes", "Customizable", "UI Components", diff --git a/lib/src/client/core/core.tsx b/lib/src/client/core/core.tsx index ad80724d..e5320b40 100644 --- a/lib/src/client/core/core.tsx +++ b/lib/src/client/core/core.tsx @@ -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 () => { @@ -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 @@ -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 @@ -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; }; diff --git a/packages/shared/CHANGELOG.md b/packages/shared/CHANGELOG.md index b32eac07..2c917232 100644 --- a/packages/shared/CHANGELOG.md +++ b/packages/shared/CHANGELOG.md @@ -1,5 +1,12 @@ # @repo/shared +## 0.0.4 + +### Patch Changes + +- Updated dependencies + - nextjs-darkmode@0.1.0 + ## 0.0.3 ### Patch Changes diff --git a/packages/shared/package.json b/packages/shared/package.json index d75bbfb3..93e347bb 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@repo/shared", - "version": "0.0.3", + "version": "0.0.4", "private": true, "sideEffects": false, "main": "./dist/index.js",