Skip to content

Commit 49e18b0

Browse files
authored
Merge pull request #25 from react18-tools/scoped-css
Add support for Scoped CSS or CSS modules
2 parents 2da0d4f + 6bc0dfd commit 49e18b0

File tree

17 files changed

+93
-39
lines changed

17 files changed

+93
-39
lines changed

.github/workflows/publish.yml

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ jobs:
4545
- run: node lite.js && pnpm build && pnpm publish-package && node scope.js && pnpm publish-package
4646
env:
4747
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
48+
OWNER: ${{ github.event.repository.owner.login }}
4849

4950
- name: Mark scoped package as deprecated
5051
run: |

examples/app-router/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# app-router
22

3+
## 0.0.20
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
- nextjs-themes@3.1.0
9+
- shared-ui@1.0.2
10+
311
## 0.0.19
412

513
### Patch Changes

examples/app-router/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "app-router",
3-
"version": "0.0.19",
3+
"version": "0.0.20",
44
"private": true,
55
"scripts": {
66
"dev": "next dev --port 3002",

examples/pages-router/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# nextjs-pages-router
22

3+
## 1.0.15
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
- nextjs-themes@3.1.0
9+
- shared-ui@1.0.2
10+
311
## 1.0.14
412

513
### Patch Changes

examples/pages-router/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pages-router",
3-
"version": "1.0.14",
3+
"version": "1.0.15",
44
"private": true,
55
"scripts": {
66
"dev": "next dev --port 3003",

examples/simple-multi-theme/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# simple-multi-theme
22

3+
## 1.0.15
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
- nextjs-themes@3.1.0
9+
- shared-ui@1.0.2
10+
311
## 1.0.14
412

513
### Patch Changes

examples/simple-multi-theme/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simple-multi-theme",
3-
"version": "1.0.14",
3+
"version": "1.0.15",
44
"private": true,
55
"scripts": {
66
"dev": "next dev --port 3001",

examples/tailwind/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# tailwind
22

3+
## 0.1.9
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
- nextjs-themes@3.1.0
9+
310
## 0.1.8
411

512
### Patch Changes

examples/tailwind/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tailwind",
3-
"version": "0.1.8",
3+
"version": "0.1.9",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

examples/vite/CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# vite-example
22

3+
## 0.0.20
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
- nextjs-themes@3.1.0
9+
- shared-ui@1.0.2
10+
311
## 0.0.19
412

513
### Patch Changes

examples/vite/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vite-example",
33
"private": true,
4-
"version": "0.0.19",
4+
"version": "0.0.20",
55
"type": "module",
66
"scripts": {
77
"dev": "vite --port 3001",

lib/nextjs-themes/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# nextjs-themes
22

3+
## 3.1.0
4+
5+
### Minor Changes
6+
7+
- Add support for CSS modules and minor fixes.
8+
39
## 3.0.0
410

511
### Major Changes

lib/nextjs-themes/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "nextjs-themes",
33
"author": "Mayank Kumar Chaudhari <https://mayank-chaudhari.vercel.app>",
44
"private": false,
5-
"version": "3.0.0",
5+
"version": "3.1.0",
66
"description": "Unleash the Power of React Server Components! Use multiple themes on your site with confidence, without losing any advantages of React Server Components.",
77
"main": "./index.ts",
88
"types": "./index.ts",

lib/nextjs-themes/src/client/color-switch/color-switch.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { act, cleanup, fireEvent, render, renderHook, screen } from "@testing-library/react";
22
import { ColorSwitch } from "./color-switch";
33
import { useTheme } from "../../hooks";
4+
import { afterEach, describe, test } from "vitest";
45

56
describe("color-switch", () => {
67
afterEach(cleanup);

lib/nextjs-themes/src/client/theme-switcher/theme-switcher.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export interface ThemeSwitcherProps {
1010
forcedColorScheme?: ColorSchemeType;
1111
targetSelector?: string;
1212
themeTransition?: string;
13+
/** provide styles object imported from CSS/SCSS modules, if you are using CSS/SCSS modules. */
14+
styles?: Record<string, string>;
1315
}
1416

1517
function useMediaQuery(setThemeState: SetStateAction<ThemeStoreType>) {
@@ -63,14 +65,16 @@ export interface UpdateProps {
6365

6466
const updateDOM = (
6567
{ resolvedTheme, resolvedColorScheme, resolvedColorSchemePref, th }: UpdateProps,
66-
targetSelector?: string,
68+
props: ThemeSwitcherProps,
6769
) => {
70+
const { targetSelector, styles } = props;
6871
const target = document.querySelector(targetSelector || `#${DEFAULT_ID}`);
72+
let classes = [resolvedColorScheme, `theme-${resolvedTheme}`, `th-${th}`, `csp-${resolvedColorSchemePref}`];
73+
if (styles) classes = classes.map(cls => styles[cls] ?? cls);
6974
/** don't apply theme to documentElement for localized targets */
7075
[target, targetSelector && target ? null : document.documentElement].forEach(target => {
7176
/** ensuring that class 'dark' is always present when dark color scheme is applied to support Tailwind */
72-
if (target)
73-
target.className = `${resolvedColorScheme} theme-${resolvedTheme} th-${th} csp-${resolvedColorSchemePref}`;
77+
if (target) target.className = classes.join(" ");
7478
target?.setAttribute("data-th", th);
7579
target?.setAttribute("data-theme", resolvedTheme);
7680
target?.setAttribute("data-color-scheme", resolvedColorScheme);
@@ -112,7 +116,7 @@ export function useThemeSwitcher(props: ThemeSwitcherProps) {
112116
const restoreTransitions = disableAnimation(props.themeTransition);
113117

114118
const resolvedData = resolveTheme(themeState, props);
115-
const shouldCreateCookie = updateDOM(resolvedData, props.targetSelector);
119+
const shouldCreateCookie = updateDOM(resolvedData, props);
116120
if (tInit < Date.now() - 300) {
117121
const stateStr = encodeState(themeState);
118122
const key = props.targetSelector || DEFAULT_ID;

lib/nextjs-themes/src/server/nextjs/server-side-wrapper/server-side-wrapper.tsx

+31-25
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,41 @@ export interface NextJsSSRThemeSwitcherProps extends HTMLProps<HTMLElement> {
1616
forcedPages?: ForcedPage[];
1717
/** id of target element to apply classes to. This is useful when you want to apply theme only to specific container. */
1818
targetId?: string;
19+
/** provide styles object imported from CSS/SCSS modules, if you are using CSS/SCSS modules. */
20+
styles?: Record<string, string>;
21+
}
22+
23+
function getDataProps(resolvedData: UpdateProps, styles?: Record<string, string>) {
24+
const dataProps: DataProps = { className: "" };
25+
let classeNames = [];
26+
if (resolvedData.resolvedColorScheme !== undefined) {
27+
dataProps["data-color-scheme"] = resolvedData.resolvedColorScheme;
28+
classeNames.push(resolvedData.resolvedColorScheme);
29+
}
30+
if (resolvedData.resolvedTheme !== undefined) {
31+
dataProps["data-theme"] = resolvedData.resolvedTheme;
32+
classeNames.push(`theme-${resolvedData.resolvedTheme}`);
33+
}
34+
if (resolvedData.th) {
35+
dataProps["data-th"] = resolvedData.th;
36+
classeNames.push(`th-${resolvedData.th}`);
37+
}
38+
if (resolvedData.resolvedColorSchemePref !== undefined) {
39+
dataProps["data-csp"] = resolvedData.resolvedColorSchemePref;
40+
classeNames.push(`csp-${resolvedData.resolvedColorSchemePref}`);
41+
}
42+
if (styles) classeNames = classeNames.map(cls => styles[cls] ?? cls);
43+
dataProps.className = classeNames.join(" ");
44+
return dataProps;
1945
}
2046

2147
function sharedServerComponentRenderer(
22-
{ children, tag, forcedPages, targetId, ...props }: NextJsSSRThemeSwitcherProps,
48+
{ children, tag, forcedPages, targetId, styles, ...props }: NextJsSSRThemeSwitcherProps,
2349
defaultTag: "div" | "html",
2450
) {
2551
const Tag: keyof JSX.IntrinsicElements = tag || defaultTag;
26-
const state = cookies().get(DEFAULT_ID)?.value;
52+
const key = targetId ? `#${targetId}` : DEFAULT_ID;
53+
const state = cookies().get(key)?.value;
2754

2855
const path = headers().get("referer");
2956
const forcedPage = forcedPages?.find(forcedPage =>
@@ -34,8 +61,8 @@ function sharedServerComponentRenderer(
3461
: forcedPage?.props;
3562
const themeState = state ? (parseState(state) as ThemeStoreType) : undefined;
3663
const resolvedData = resolveTheme(themeState, forcedPageProps);
37-
const dataProps = getDataProps(resolvedData);
38-
if(targetId) dataProps.className += " nth-scoped";
64+
const dataProps = getDataProps(resolvedData, styles);
65+
if (targetId) dataProps.className += styles?.[" nth-scoped"] ?? " nth-scoped";
3966

4067
return (
4168
// @ts-expect-error -> svg props and html element props conflict
@@ -45,27 +72,6 @@ function sharedServerComponentRenderer(
4572
);
4673
}
4774

48-
function getDataProps(resolvedData: UpdateProps) {
49-
const dataProps: DataProps = { className: "" };
50-
if (resolvedData.resolvedColorScheme !== undefined) {
51-
dataProps["data-color-scheme"] = resolvedData.resolvedColorScheme;
52-
dataProps.className = resolvedData.resolvedColorScheme;
53-
}
54-
if (resolvedData.resolvedTheme !== undefined) {
55-
dataProps["data-theme"] = resolvedData.resolvedTheme;
56-
dataProps.className += `theme-${resolvedData.resolvedTheme}`;
57-
}
58-
if (resolvedData.th) {
59-
dataProps["data-th"] = resolvedData.th;
60-
dataProps.className += ` th-${resolvedData.th}`;
61-
}
62-
if (resolvedData.resolvedColorSchemePref !== undefined) {
63-
dataProps["data-csp"] = resolvedData.resolvedColorSchemePref;
64-
dataProps.className += ` csp-${resolvedData.resolvedColorSchemePref}`;
65-
}
66-
return dataProps;
67-
}
68-
6975
/**
7076
* @example
7177
* ```tsx

lib/nextjs-themes/vitest.setup.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import useRGS from "r18gs";
2-
import { vi, beforeEach } from "vitest";
3-
import { DEFAULT_ID, initialState } from "./src/constants";
4-
import { act, renderHook } from "@testing-library/react";
1+
import { vi } from "vitest";
52

63
// mock matchMedia
74
Object.defineProperty(window, "matchMedia", {

0 commit comments

Comments
 (0)