diff --git a/.changeset/brown-laws-perform.md b/.changeset/brown-laws-perform.md new file mode 100644 index 00000000..89500801 --- /dev/null +++ b/.changeset/brown-laws-perform.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Updaet patch diff --git a/.changeset/clean-turtles-nail.md b/.changeset/clean-turtles-nail.md new file mode 100644 index 00000000..db1d548d --- /dev/null +++ b/.changeset/clean-turtles-nail.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Touchup diff --git a/.changeset/fuzzy-timers-lie.md b/.changeset/fuzzy-timers-lie.md new file mode 100644 index 00000000..14673128 --- /dev/null +++ b/.changeset/fuzzy-timers-lie.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": major +--- + +Do not export force-theme and force-color-scheme components from root. diff --git a/.changeset/good-cobras-deny.md b/.changeset/good-cobras-deny.md new file mode 100644 index 00000000..5592d8b6 --- /dev/null +++ b/.changeset/good-cobras-deny.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Patch alpha to avoid publishing over previously published version diff --git a/.changeset/mighty-toes-boil.md b/.changeset/mighty-toes-boil.md new file mode 100644 index 00000000..3fac3b5c --- /dev/null +++ b/.changeset/mighty-toes-boil.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Test bundle size by re-exporting forced components. diff --git a/.changeset/moody-eggs-report.md b/.changeset/moody-eggs-report.md new file mode 100644 index 00000000..eeda6199 --- /dev/null +++ b/.changeset/moody-eggs-report.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Minify CSS diff --git a/.changeset/plenty-chicken-provide.md b/.changeset/plenty-chicken-provide.md new file mode 100644 index 00000000..afcb4a0d --- /dev/null +++ b/.changeset/plenty-chicken-provide.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Remove rarely used exports diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000..3d762529 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,25 @@ +{ + "mode": "exit", + "tag": "alpha", + "initialVersions": { + "@example/app": "0.0.0", + "@example/pages": "0.0.0", + "@example/vite": "0.0.0", + "nextjs-themes": "4.0.0-alpha.7", + "@repo/eslint-config": "0.0.0", + "@repo/typescript-config": "0.0.0", + "@repo/shared": "0.0.6-alpha.7", + "@repo/scripts": "0.0.0" + }, + "changesets": [ + "brown-laws-perform", + "clean-turtles-nail", + "fuzzy-timers-lie", + "good-cobras-deny", + "mighty-toes-boil", + "moody-eggs-report", + "plenty-chicken-provide", + "purple-waves-marry", + "twenty-paws-bathe" + ] +} diff --git a/.changeset/purple-waves-marry.md b/.changeset/purple-waves-marry.md new file mode 100644 index 00000000..d71f6623 --- /dev/null +++ b/.changeset/purple-waves-marry.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Avoid rerendering without memo. Attempt to minify minzip further diff --git a/.changeset/twenty-paws-bathe.md b/.changeset/twenty-paws-bathe.md new file mode 100644 index 00000000..96971d69 --- /dev/null +++ b/.changeset/twenty-paws-bathe.md @@ -0,0 +1,5 @@ +--- +"nextjs-themes": patch +--- + +Import hook and color-switch from their own files. diff --git a/.tkb b/.tkb index 4d993d25..a56449c3 100644 --- a/.tkb +++ b/.tkb @@ -1,11 +1,19 @@ { "scope": "Workspace", - "tasks": {}, + "tasks": { + "0o1UME_ZgqZdcbYksPgDI": { + "id": "0o1UME_ZgqZdcbYksPgDI", + "description": "Update Docs\n- Use wiki - Export typedoc as md and then publish to wiki", + "columnId": "column-todo" + } + }, "columns": [ { "id": "column-todo", "title": "To do", - "tasksIds": [] + "tasksIds": [ + "0o1UME_ZgqZdcbYksPgDI" + ] }, { "id": "column-doing", diff --git a/README.md b/README.md index 68b14a89..0decce33 100644 --- a/README.md +++ b/README.md @@ -45,267 +45,12 @@ This project was inspired by next-themes. Unlike next-themes, `nextjs-themes` do > Check out the [live example](https://nextjs-themes.vercel.app/). -
-

Installation

- -```bash -$ pnpm add nextjs-themes -``` - -**_or_** - -```bash -$ npm install nextjs-themes -``` - -**_or_** - -```bash -$ yarn add nextjs-themes -``` - -
- -
-

Want Lite Version?

- -[![npm bundle size](https://img.shields.io/bundlephobia/minzip/nextjs-themes-lite)](https://www.npmjs.com/package/nextjs-themes-lite) [![Version](https://img.shields.io/npm/v/nextjs-themes-lite.svg?colorB=green)](https://www.npmjs.com/package/nextjs-themes-lite) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/nextjs-themes-lite.svg)](https://www.npmjs.com/package/nextjs-themes-lite) - -
- -```bash -$ pnpm add nextjs-themes-lite -``` - -**or** - -```bash -$ npm install nextjs-themes-lite -``` - -**or** - -```bash -$ yarn add nextjs-themes-lite -``` - -> Note: `r18gs` is a peer dependency - -
- -## Usage - -### SPA (e.g., Vite, CRA) and Next.js pages directory (No server components) - -To add dark mode support, modify `_app.js` as follows: - -```js -import { ThemeSwitcher } from "nextjs-themes"; - -function MyApp({ Component, pageProps }) { - return ( - <> - - - - ); -} - -export default MyApp; -``` - -⚡🎉Boom! Dark mode is ready in just a couple of lines! - -### With Next.js `app` router (Server Components) - -Update `app/layout.jsx` to add `ThemeSwitcher` from `nextjs-themes`: - -```tsx -// app/layout.jsx -import { ThemeSwitcher } from "nextjs-themes"; - -export default function Layout({ children }) { - return ( - - - - - {children} - - - ); -} -``` - -Woohoo! Multiple theme modes with Server Components support! - -### HTML & CSS - -Next.js app supports dark mode, including System preference with `prefers-color-scheme`. The theme is synced between tabs, modifying the `data-theme` attribute on the `html` element: - -```css -:root { - --background: white; - --foreground: black; -} - -[data-theme="dark"] { - --background: black; - --foreground: white; -} -``` - -## Images - -Show different images based on the current theme: - -```ts -import Image from "next/image"; -import { useTheme } from "nextjs-themes"; - -function ThemedImage() { - const { resolvedTheme } = useTheme(); - const src = resolvedTheme === "light" ? "/light.png" : "/dark.png"; - return ; -} - -export default ThemedImage; -``` - -### useTheme - -The `useTheme` hook provides theme information and allows changing the theme: - -```js -import { useTheme } from "nextjs-themes"; - -const ThemeChanger = () => { - const { theme, setTheme } = useTheme(); - - return ( -
- The current theme is: {theme} - - -
- ); -}; -``` - -The `useTheme` hook returns the following object: - -```tsx -interface UseThemeYield { - theme: string; - darkTheme: string; - lightTheme: string; - colorSchemePref: ColorSchemeType; - systemColorScheme: ResolvedColorSchemeType; - resolvedColorScheme: ResolvedColorSchemeType; - resolvedTheme: string; - setTheme: (theme: string) => void; - setDarkTheme: (darkTheme: string) => void; - setLightTheme: (lightTheme: string) => void; - setThemeSet: (themeSet: { darkTheme: string; lightTheme: string }) => void; - setColorSchemePref: (colorSchemePref: ColorSchemeType) => void; - toggleColorScheme: (skipSystem?: boolean) => void; - setForcedTheme: (forcedTheme: string) => void; - setForcedColorScheme: (forcedColorScheme: ColorSchemeType) => void; -} -``` - -
-

Force per page theme and color-scheme

- -### Next.js App Router - -```tsx -import { ForceTheme } from "nextjs-themes/force-theme"; - -function MyPage() { - return ( - <> - - ... - - ); -} - -export default MyPage; -``` - -> If you are using TypeScript and have not set nodeResolution to `Bundler` or `Node16` or `NodeNext`, you need to import from `nextjs-themes/client/force-theme` +## Getting Started -### Next.js Pages Router +> See [Getting Started](./guides/getting-started.md) -For the pages router, you have two options. The first option is the same as the app router, and the second option, which is compatible with `next-themes`, involves adding the `theme` property to your page component like this: - -```javascript -function MyPage() { - return <>...; -} - -MyPage.theme = "my-theme"; - -export default MyPage; -``` - -Similarly, you can force a color scheme. This will apply your `defaultDark` or `defaultLight` theme, which can be configured via hooks. - -
- -### With Styled Components and any CSS-in-JS - -Next Themes works with any library. For Styled Components, `createGlobalStyle` in your custom App: - -```js -// pages/_app.js -import { createGlobalStyle } from "styled-components"; -import { ThemeSwitcher } from "nextjs-themes"; - -const GlobalStyle = createGlobalStyle` - :root { - --fg: #000; - --bg: #fff; - } - - [data-theme="dark"] { - --fg: #fff; - --bg: #000; - } -`; - -function MyApp({ Component, pageProps }) { - return ( - <> - - - - - ); -} -``` - -### With Tailwind - -In `tailwind.config.js`, set the dark mode property to class: - -```js -// tailwind.config.js -module.exports = { - darkMode: "class", -}; -``` - -⚡🎉Ready to use dark mode in Tailwind! - -> Caution: Your class must be `"dark"`, which is the default value used in this library. Tailwind requires the class name `"dark"` for dark-theme. - -Use dark-mode specific classes: - -```tsx -

-``` +> Want Lite Version? [![npm bundle size](https://img.shields.io/bundlephobia/minzip/nextjs-themes-lite)](https://www.npmjs.com/package/nextjs-themes-lite) [![Version](https://img.shields.io/npm/v/nextjs-themes-lite.svg?colorB=green)](https://www.npmjs.com/package/nextjs-themes-lite) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/nextjs-themes-lite.svg)](https://www.npmjs.com/package/nextjs-themes-lite) +> In case you are using `r18gs` in your project, you may use lite version which requires `r18gs` as a peerDependency. ## Migration diff --git a/contributing.md b/contributing.md index 39256cad..c3b280d5 100644 --- a/contributing.md +++ b/contributing.md @@ -4,39 +4,43 @@ ### Included Utilities -This template is equipped with pre-configured tools to streamline your development process: - -- Monorepo setup powered by TurboRepo - - TurboRepo is renowned for its efficient builds and caching mechanisms, minimizing unnecessary builds. -- [TypeScript](https://www.typescriptlang.org/) for static type checking -- [ESLint](https://eslint.org/) for code linting -- [Prettier](https://prettier.io) for code formatting -- Plop-based code generator for effortlessly scaffolding new components -- Automatic rebranding functionality for this template -- Workflows facilitating testing, documentation, dependency updates, and deployment of your docs and packages -- Build setup capable of creating appropriate CJS and ESM builds to support React 18 server and client component exports from the same library -- Out-of-the-box support for SCSS modules for `lib` and `packages/shared` +This project is configured with a suite of pre-configured tools to enhance development experience: + +- **Monorepo setup with TurboRepo**: + - TurboRepo provides efficient builds and caching, reducing unnecessary rebuilds. +- [TypeScript](https://www.typescriptlang.org/) for static type checking. +- [ESLint](https://eslint.org/) for code linting. +- [Prettier](https://prettier.io) for code formatting. +- A Plop-based code generator for easy component scaffolding. +- Workflows for testing, documentation, dependency updates, and deployment of docs and packages. +- Build setup for generating both CJS and ESM builds to support React 18 server and client component exports from the same library. +- Native support for SCSS modules in `lib` and `packages/shared`. ### Apps and Packages -This TurboRepo comprises the following packages/examples, all written in [TypeScript](https://www.typescriptlang.org/): +This TurboRepo includes the following packages/examples, all in [TypeScript](https://www.typescriptlang.org/): -- `@example/nextjs`: a [Next.js](https://nextjs.org/) app -- `@example/vite`: a [Vite.js](https://vitest.dev) app -- `@example/remix`: a Remix app -- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) -- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo -- `@repo/jest-presets`: Jest presets for unit testing -- `@repo/shared`: An internal library of components utilized by the examples -- `react18-loaders`: a React component library (The core package published to NPM) +- **`nextjs-themes`**: The core React component library (published to NPM). +- **`@example/app-router`**: A [Next.js](https://nextjs.org/) example app using the app router/directory. +- **`@example/pages-router`**: A [Next.js](https://nextjs.org/) example app using the pages router. +- **`@example/vite`**: A [Vite.js](https://vitejs.dev) app. +- **`@repo/config-eslint`**: ESLint configurations (includes `eslint-config-next` and `eslint-config-prettier`). +- **`@repo/config-typescript`**: `tsconfig.json` configurations used throughout the monorepo. +- **`@repo/shared`**: An internal library of components used by the examples. ## Automated File Generation -Simply execute `yarn turbo gen` and follow the prompts to automatically generate your new component along with a test file and dependency linking, adhering to best practices. +To automatically generate a new component along with a test file and dependency linking, run: + +```bash +yarn plop +``` + +Follow the prompts to ensure adherence to best practices. ### Build -To build all apps and packages, execute the following command: +To build all apps and packages, run: ```bash pnpm build @@ -44,7 +48,7 @@ pnpm build ### Development -For development of all apps and packages, run: +For developing all apps and packages, use: ```bash pnpm dev @@ -52,7 +56,7 @@ pnpm dev ### Running Unit Tests -To execute unit tests, use: +To run unit tests, execute: ```bash pnpm test @@ -60,7 +64,7 @@ pnpm test ### Linting and Formatting -Before creating a PR, ensure that linting passes and format the code properly with: +Before submitting a PR, ensure your code passes linting and is properly formatted by running: ```bash pnpm lint @@ -74,9 +78,9 @@ pnpm format ## Useful Resources -Explore more about TurboRepo and Next.js through the following links: +Learn more about TurboRepo and Next.js through these links: -- [React and Next.js with TypeScript](https://www.udemy.com/course/react-and-next-js-with-typescript/?referralCode=7202184A1E57C3DCA8B2) - an interactive Next.js course. +- [React and Next.js with TypeScript](https://www.udemy.com/course/react-and-next-js-with-typescript/?referralCode=7202184A1E57C3DCA8B2) - An interactive Next.js course. - [The Game of Chess with Next.js, React, and TypeScript](https://www.udemy.com/course/game-of-chess-with-nextjs-react-and-typescrypt/?referralCode=851A28F10B254A8523FE) - [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks) - [Caching](https://turbo.build/repo/docs/core-concepts/caching) @@ -85,7 +89,11 @@ Explore more about TurboRepo and Next.js through the following links: - [Configuration Options](https://turbo.build/repo/docs/reference/configuration) - [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference) -> Quick tip: Remove all stale branches with `git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d` +> **Quick tip**: Remove all stale branches with: +> +> ```bash +> git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d +> ``` > Consider enrolling in [our courses](https://mayank-chaudhari.vercel.app/courses) or [sponsoring](https://github.com/sponsors/mayank1513) our work. diff --git a/examples/app-router/src/app/button.module.css b/examples/app-router/src/app/button.module.css deleted file mode 100644 index 54c39828..00000000 --- a/examples/app-router/src/app/button.module.css +++ /dev/null @@ -1,15 +0,0 @@ -.btn { - display: block; - margin: auto; - margin-bottom: 3rem; - cursor: pointer; - padding: 15px 20px; - background: transparent; - border-radius: 10px; - color: var(--text-color); - box-shadow: -2px 2px 8px var(--text-color); -} - -.btn:hover { - box-shadow: none; -} diff --git a/examples/app-router/src/app/button.tsx b/examples/app-router/src/app/button.tsx deleted file mode 100644 index 8ea89880..00000000 --- a/examples/app-router/src/app/button.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import { useCallback } from "react"; -import { useLoader } from "react18-loaders"; -import styles from "./button.module.css"; - -/** Button to show global loader for 3 sec. */ -export default function MyButton() { - const { setLoading } = useLoader(); - const handleClick = useCallback(() => { - setLoading(true); - setTimeout(() => { - setLoading(false); - }, 3000); - }, []); - return ( - - ); -} diff --git a/examples/app-router/src/app/layout.tsx b/examples/app-router/src/app/layout.tsx index 098ee468..a4258407 100644 --- a/examples/app-router/src/app/layout.tsx +++ b/examples/app-router/src/app/layout.tsx @@ -1,5 +1,4 @@ import "./styles.css"; -import "react18-loaders/dist/index.css"; import { ThemeSwitcher } from "nextjs-themes"; import { Layout } from "@repo/shared/dist/server"; import { GlobalLoader, Header } from "@repo/shared"; @@ -7,7 +6,7 @@ import { Inter } from "next/font/google"; import Link from "next/link"; import { Cards, LandingPage } from "@repo/shared/dist/server"; import { PageNavigatorCard, ThemeController } from "@repo/shared"; -import { ColorSwitch } from "nextjs-themes"; +import { ColorSwitch } from "nextjs-themes/color-switch"; const inter = Inter({ subsets: ["latin"] }); diff --git a/examples/pages-router/src/pages/_app.tsx b/examples/pages-router/src/pages/_app.tsx index 02b30810..a21467e5 100644 --- a/examples/pages-router/src/pages/_app.tsx +++ b/examples/pages-router/src/pages/_app.tsx @@ -8,7 +8,7 @@ import { Header } from "@repo/shared"; import Link from "next/link"; import { Card, Cards, LandingPage } from "@repo/shared/dist/server"; import { ThemeController, PageNavigatorCard } from "@repo/shared"; -import { ColorSwitch } from "nextjs-themes"; +import { ColorSwitch } from "nextjs-themes/color-switch"; const inter = Inter({ subsets: ["latin"] }); diff --git a/examples/vite/src/app/index.tsx b/examples/vite/src/app/index.tsx index 2d13ec12..b686dce5 100644 --- a/examples/vite/src/app/index.tsx +++ b/examples/vite/src/app/index.tsx @@ -1,7 +1,7 @@ import React from "react"; import "./styles.css"; import { Cards, LandingPage } from "@repo/shared/dist/server"; -import { ColorSwitch } from "nextjs-themes"; +import { ColorSwitch } from "nextjs-themes/color-switch"; import { Header, PageNavigatorCard, ThemeController } from "@repo/shared"; import { Link } from "react-router-dom"; diff --git a/guides/getting-started.md b/guides/getting-started.md new file mode 100644 index 00000000..2511b131 --- /dev/null +++ b/guides/getting-started.md @@ -0,0 +1,308 @@ +--- +title: Getting Started +--- + +# Getting Started + +## Installation + +```bash +$ pnpm add nextjs-themes +``` + +**_or_** + +```bash +$ npm install nextjs-themes +``` + +**_or_** + +```bash +$ yarn add nextjs-themes +``` + +### Want Lite Version? + +[![npm bundle size](https://img.shields.io/bundlephobia/minzip/nextjs-themes-lite)](https://www.npmjs.com/package/nextjs-themes-lite) [![Version](https://img.shields.io/npm/v/nextjs-themes-lite.svg?colorB=green)](https://www.npmjs.com/package/nextjs-themes-lite) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/d18m/nextjs-themes-lite.svg)](https://www.npmjs.com/package/nextjs-themes-lite) + +```bash +$ pnpm add nextjs-themes-lite +``` + +**or** + +```bash +$ npm install nextjs-themes-lite +``` + +**or** + +```bash +$ yarn add nextjs-themes-lite +``` + +> Note: `r18gs` is a peer dependency + +## Simple Usage + +### SPA (e.g., Vite, CRA) and Next.js pages directory (No server components) + +To add dark mode support, modify `_app.js` as follows: + +```js +import { ThemeSwitcher } from "nextjs-themes"; + +function MyApp({ Component, pageProps }) { + return ( + <> + + + + ); +} + +export default MyApp; +``` + +⚡🎉Boom! Dark mode is ready in just a couple of lines! + +### With Next.js `app` router (Server Components) + +Update `app/layout.jsx` to add `ThemeSwitcher` from `nextjs-themes`: + +```tsx +// app/layout.jsx +import { ThemeSwitcher } from "nextjs-themes"; + +export default function Layout({ children }) { + return ( + + + + + {children} + + + ); +} +``` + +Woohoo! Multiple theme modes with Server Components support! + +### HTML & CSS + +Next.js app supports dark mode, including System preference with `prefers-color-scheme`. The theme is synced between tabs, modifying the `data-theme` attribute on the `html` element: + +```css +:root { + --background: white; + --foreground: black; +} + +[data-theme="dark"] { + --background: black; + --foreground: white; +} +``` + +## Configuration + +Configuring nextjs-themes is super simple. You can use following props to configure the main `ThemeSwitcher` component. + +```ts +export interface ThemeSwitcherProps { + /** + * Forced theme name for the current page + * @see [Force per page theme and color-scheme](https://github.com/react18-tools/nextjs-themes?tab=readme-ov-file#force-per-page-theme-and-color-scheme) + */ + forcedTheme?: string; + /** + * Forced color scheme for the current page + * @see [Force per page theme and color-scheme](https://github.com/react18-tools/nextjs-themes?tab=readme-ov-file#force-per-page-theme-and-color-scheme) + */ + forcedColorScheme?: ColorSchemeType; + /** + * CSS selector for the target element to apply the theme. + * Use this to specify a different target element than the default (html or documentElement). + * This is particularly useful for controlling the theme of different parts of the page independently. + */ + targetSelector?: string; + /** + * The transition property to enforce on all elements, preventing unwanted transitions during theme changes. + * @example 'background .3s' + * @defaultValue 'none' + */ + themeTransition?: string; + /** + * Provide a styles object imported from CSS/SCSS modules if you are using these modules to define theme and color-scheme classes. + * All classes applied to the target are modified using the styles object as follows: + * `if (styles) classes = classes.map(cls => styles[cls] ?? cls);` + */ + styles?: Record; + /** The nonce value for your Content Security Policy. */ + nonce?: string; +} +``` + +To augment the functionality, we also provide `ForceTheme` and `ForceColorScheme` components that effectively apply the `forcedTheme` and `forcedColorScheme` props via internal state variable. Please note that the props passed to the `ThemeSwitcher` has the highest priority. + +## Handling different scenarios + +### Images + +Show different images based on the current theme: + +```ts +import Image from "next/image"; +import { useTheme } from "nextjs-themes/hooks"; + +function ThemedImage() { + const { resolvedTheme } = useTheme(); + const src = resolvedTheme === "light" ? "/light.png" : "/dark.png"; + return ; +} + +export default ThemedImage; +``` + +### useTheme + +The `useTheme` hook provides theme information and allows changing the theme: + +```js +import { useTheme } from "nextjs-themes/hooks"; + +const ThemeChanger = () => { + const { theme, setTheme } = useTheme(); + + return ( +
+ The current theme is: {theme} + + +
+ ); +}; +``` + +The `useTheme` hook returns the following object: + +```tsx +interface UseThemeYield { + theme: string; + darkTheme: string; + lightTheme: string; + colorSchemePref: ColorSchemeType; + systemColorScheme: ResolvedColorSchemeType; + resolvedColorScheme: ResolvedColorSchemeType; + resolvedTheme: string; + setTheme: (theme: string) => void; + setDarkTheme: (darkTheme: string) => void; + setLightTheme: (lightTheme: string) => void; + setThemeSet: (themeSet: { darkTheme: string; lightTheme: string }) => void; + setColorSchemePref: (colorSchemePref: ColorSchemeType) => void; + toggleColorScheme: (skipSystem?: boolean) => void; + setForcedTheme: (forcedTheme: string) => void; + setForcedColorScheme: (forcedColorScheme: ColorSchemeType) => void; +} +``` + +
+

Force per page theme and color-scheme

+ +### Next.js App Router + +```tsx +import { ForceTheme } from "nextjs-themes/force-theme"; + +function MyPage() { + return ( + <> + + ... + + ); +} + +export default MyPage; +``` + +> If you are using TypeScript and have not set nodeResolution to `Bundler` or `Node16` or `NodeNext`, you need to import from `nextjs-themes/client/force-theme` + +### Next.js Pages Router + +For the pages router, you have two options. The first option is the same as the app router, and the second option, which is compatible with `next-themes`, involves adding the `theme` property to your page component like this: + +```javascript +function MyPage() { + return <>...; +} + +MyPage.theme = "my-theme"; + +export default MyPage; +``` + +Similarly, you can force a color scheme. This will apply your `defaultDark` or `defaultLight` theme, which can be configured via hooks. + +
+ +### With Styled Components and any CSS-in-JS + +Next Themes works with any library. For Styled Components, `createGlobalStyle` in your custom App: + +```js +// pages/_app.js +import { createGlobalStyle } from "styled-components"; +import { ThemeSwitcher } from "nextjs-themes"; + +const GlobalStyle = createGlobalStyle` + :root { + --fg: #000; + --bg: #fff; + } + + [data-theme="dark"] { + --fg: #fff; + --bg: #000; + } +`; + +function MyApp({ Component, pageProps }) { + return ( + <> + + + + + ); +} +``` + +### With Tailwind + +In `tailwind.config.js`, set the dark mode property to class: + +```js +// tailwind.config.js +module.exports = { + darkMode: "class", +}; +``` + +⚡🎉Ready to use dark mode in Tailwind! + +> Caution: Your class must be `"dark"`, which is the default value used in this library. Tailwind requires the class name `"dark"` for dark-theme. + +Use dark-mode specific classes: + +```tsx +

+``` + +> Please consider enrolling in [our courses](https://mayank-chaudhari.vercel.app/courses) or [sponsoring](https://github.com/sponsors/mayank1513) our work. + +
+ +

with 💖 by Mayank Kumar Chaudhari

diff --git a/guides/migration.md b/guides/migration.md index b982dc55..26134fd8 100644 --- a/guides/migration.md +++ b/guides/migration.md @@ -1,41 +1,60 @@ +--- +title: Migration Guide +--- + # Migration Guide -## Migrating from v1 to v2 +## Migrating to V3 and V4 + +> V3 was entirely re-written with minimal API changes. V4 optimises V3 by minor refactors. + +- No more cookies. +- You no longer need to use `NextJsSSGThemeSwitcher`, `NextJsServerTarget`, or `ServerSideWrapper`. +- Flash of Unstyled Content (FOUC) is now handled by an injected script. +- If you have been using these components, they will have no effect. We recommend removing them. +- There is no need to use sibling selectors. Without `NextJsSSGThemeSwitcher` or `NextJsServerTarget`, you are free to use any target, whether as a wrapper or a sibling. + +### Breaking Changes - these should only affect a very small fraction of library users + +- `ForceColorScheme` and `ForceTheme` are no longer exported from `nextjs-themes` or `nextjs-themes/client`. Use `nextjs-themes/force-color-scheme` or `nextjs-themes/force-theme` instead. +- The class names for `ColorSwitch` have been shortened. + +## Migrating from V1 to V2 ### Major Changes -- 6f17cce: # Additonal CSS Combinations + Ensure seamless support for Tailwind +- Commit `6f17cce`: Added additional CSS combinations to ensure seamless support for Tailwind. - - No changes required for client side code as `[data-theme=]` selectors work as before. - - If you are using `ServerSideWrapper` or `NextJsServerTarget` or `NextJsSSGThemeSwitcher`, you need to convert `forcedPages` elements to objects of the shape `{ pathMatcher: RegExp | string; props: ThemeSwitcherProps }`. - - Use `resolvedColorScheme` for more sturdy dark/light/system modes - - Use combinations of `[data-th=""]` and `[data-color-scheme=""]` for dark/light varients of themes - - Use `[data-csp=""]` to style based on colorSchemePreference. + - No changes are required for client-side code as `[data-theme=]` selectors function as before. + - If you are using `ServerSideWrapper`, `NextJsServerTarget`, or `NextJsSSGThemeSwitcher`, you need to convert `forcedPages` elements to objects shaped like `{ pathMatcher: RegExp | string; props: ThemeSwitcherProps }`. + - Use `resolvedColorScheme` for more robust dark/light/system modes. + - Use combinations of `[data-th=""]` and `[data-color-scheme=""]` for dark/light theme variants. + - Use `[data-csp=""]` to style based on color scheme preference. ### Minor Changes -- # Support custom themeTransition +- Support for custom `themeTransition`. - - Provide `themeTransition` prop to `ThemeSwitcher` component to apply smooth transition while changing theme. + - Provide the `themeTransition` prop to the `ThemeSwitcher` component to apply a smooth transition when changing the theme. - Use `setThemeSet` to set `lightTheme` and `darkTheme` together. -#### Motivation: +#### Motivation -For server side syncing, we need to use cookies and headers. This means that this component and its children can not be static. They will be rendered server side for each request. Thus, we are avoiding the wrapper. Now, only the `NextJsSSGThemeSwitcher` will be rendered server side for each request and rest of your app can be server statically. +For server-side syncing, cookies and headers are required. This means that this component and its children cannot be static and will be rendered server-side for each request. To avoid the wrapper, only the `NextJsSSGThemeSwitcher` will be rendered server-side for each request, while the rest of your app can be served statically. -Take care of the following while migrating to `v2`. +Consider the following when migrating to V2: -- No changes required for projects not using `Next.js` app router or server components other than updating cookies policy if needed. -- The persistent storage is realized with `cookies` in place of `localStorage`. (You might want to update cookies policy accordingly.) -- We have provided `NextJsSSGThemeSwitcher` in addition to `ServerSideWrapper` for `Next.js`. You no longer need to use a wrapper component which broke static generation and forced SSR. -- Visit [With Next.js `app` router (Server Components)](#with-nextjs-app-router-server-components) +- No changes are required for projects not using the `Next.js` app router or server components, aside from updating the cookies policy if needed. +- Persistent storage now uses cookies instead of `localStorage`. (You might need to update your cookies policy accordingly.) +- We have introduced `NextJsSSGThemeSwitcher` in addition to `ServerSideWrapper` for `Next.js`. You no longer need to use a wrapper component that breaks static generation and forces SSR. +- For more details, visit [With Next.js `app` router (Server Components)](#with-nextjs-app-router-server-components). -## Migrating from v0 to v1 +## Migrating from V0 to V1 -- `defaultDarkTheme` is renamed to `darkTheme` -- `setDefaultDarkTheme` is renamed to `setDarkTheme` -- `defaultLightTheme` is renamed to `lightTheme` -- `setDefaultLightTheme` is renamed to `setLightTheme` +- `defaultDarkTheme` has been renamed to `darkTheme`. +- `setDefaultDarkTheme` has been renamed to `setDarkTheme`. +- `defaultLightTheme` has been renamed to `lightTheme`. +- `setDefaultLightTheme` has been renamed to `setLightTheme`.
diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 108f2f39..8c46b559 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,59 @@ # nextjs-themes +## 4.0.0-alpha.8 + +### Patch Changes + +- c72dd2d: Import hook and color-switch from their own files. + +## 4.0.0-alpha.7 + +### Patch Changes + +- 691038e: Remove rarely used exports + +## 4.0.0-alpha.6 + +### Patch Changes + +- c21dd8f: Minify CSS + +## 4.0.0-alpha.5 + +### Patch Changes + +- dfe139c: Updaet patch + +## 4.0.0-alpha.4 + +### Patch Changes + +- 66038c5: Test bundle size by re-exporting forced components. + +## 4.0.0-alpha.3 + +### Patch Changes + +- a31d6e0: Touchup + +## 4.0.0-alpha.2 + +### Patch Changes + +- b8164f7: Avoid rerendering without memo. Attempt to minify minzip further + +## 4.0.0-alpha.1 + +### Patch Changes + +- 4ca22c1: Patch alpha to avoid publishing over previously published version + +## 4.0.0-alpha.0 + +### Major Changes + +- aa3d50e: Do not export force-theme and force-color-scheme components from root. + ## 3.1.7 ### Patch Changes diff --git a/lib/README.md b/lib/README.md index 68b14a89..cccff724 100644 --- a/lib/README.md +++ b/lib/README.md @@ -161,7 +161,7 @@ Show different images based on the current theme: ```ts import Image from "next/image"; -import { useTheme } from "nextjs-themes"; +import { useTheme } from "nextjs-themes/hooks"; function ThemedImage() { const { resolvedTheme } = useTheme(); @@ -177,7 +177,7 @@ export default ThemedImage; The `useTheme` hook provides theme information and allows changing the theme: ```js -import { useTheme } from "nextjs-themes"; +import { useTheme } from "nextjs-themes/hooks"; const ThemeChanger = () => { const { theme, setTheme } = useTheme(); diff --git a/lib/package.json b/lib/package.json index 11aeb28d..57f1a51e 100644 --- a/lib/package.json +++ b/lib/package.json @@ -2,7 +2,7 @@ "name": "nextjs-themes", "author": "Mayank Kumar Chaudhari ", "private": false, - "version": "3.1.7", + "version": "4.0.0-alpha.8", "description": "Unleash the Power of React Server Components! Use multiple themes on your site with confidence, without losing any advantages of React Server Components.", "license": "MPL-2.0", "main": "./dist/index.js", @@ -91,9 +91,9 @@ "import": "./dist/hooks/index.mjs", "types": "./dist/hooks/index.d.ts" }, - "./styles.css": "./dist/index.css", - "./styles": "./dist/index.css", - "./css": "./dist/index.css" + "./styles.css": "./dist/client/color-switch/index.css", + "./styles": "./dist/client/color-switch/index.css", + "./css": "./dist/client/color-switch/index.css" }, "scripts": { "build": "tsup && tsc -p tsconfig-build.json", diff --git a/lib/src/client/color-switch/color-switch.module.scss b/lib/src/client/color-switch/color-switch.module.scss index 8a10d3f1..d0a27ad4 100644 --- a/lib/src/client/color-switch/color-switch.module.scss +++ b/lib/src/client/color-switch/color-switch.module.scss @@ -1,4 +1,4 @@ -.color-switch { +.s { all: unset; position: relative; box-shadow: none; @@ -6,46 +6,46 @@ border-radius: 50%; border: 1px dashed gray; cursor: pointer; - --size: 25px; - height: var(--size); - width: var(--size); + --s: 25px; + height: var(--s); + width: var(--s); transition: all 0.3s ease-in-out 0s !important; } -[data-csp="system"] .color-switch::after, -[data-csp="system"] ~ .color-switch::after, -[data-csp="system"] ~ * .color-switch::after { +[data-csp="system"] .s::after, +[data-csp="system"] ~ .s::after, +[data-csp="system"] ~ * .s::after { position: absolute; height: 100%; width: 100%; top: 0; left: 0; font-weight: 600; - font-size: calc(1 * var(--size) / 2); + font-size: calc(1 * var(--s) / 2); display: flex; align-items: center; justify-content: center; content: "A"; } -[data-csp="dark"] .color-switch, -[data-csp="dark"] ~ .color-switch, -[data-csp="dark"] ~ * .color-switch { - box-shadow: calc(var(--size) / 4) calc(var(--size) / -4) calc(var(--size) / 8) inset #fff; +[data-csp="dark"] .s, +[data-csp="dark"] ~ .s, +[data-csp="dark"] ~ * .s { + box-shadow: calc(var(--s) / 4) calc(var(--s) / -4) calc(var(--s) / 8) inset #fff; border: none; background: transparent; - animation: swing linear 0.5s; + animation: a linear 0.5s; } -[data-csp="light"] .color-switch, -[data-csp="light"] ~ .color-switch, -[data-csp="light"] ~ * .color-switch { +[data-csp="light"] .s, +[data-csp="light"] ~ .s, +[data-csp="light"] ~ * .s { box-shadow: 0 0 50px 10px yellow; background-color: yellow; border: 1px solid orangered; } -@keyframes swing { +@keyframes a { 40% { transform: rotate(-15deg); } diff --git a/lib/src/client/color-switch/color-switch.tsx b/lib/src/client/color-switch/color-switch.tsx index 1d21cd7f..1f14260d 100644 --- a/lib/src/client/color-switch/color-switch.tsx +++ b/lib/src/client/color-switch/color-switch.tsx @@ -33,7 +33,7 @@ export const ColorSwitch = ({ }: ColorSwitchProps) => { const { toggleColorScheme } = useTheme(targetSelector); - const cls = [styles["color-switch"], className].join(" "); + const cls = [styles.s, className].join(" "); return (