Skip to content

Commit ebc1150

Browse files
jantimonsamcx
andauthored
add support for cssmodules-pure-no-check to allow global CSS features like View Transitions (#77321)
## What? This PR adds support for `cssmodules-pure-no-check` in CSS Modules by upgrading `postcss-modules-local-by-default` from 4.0.4 to 4.2.0 (`cssmodules-pure-no-check` was also recently [merged into LightningCSS 1.29.3](parcel-bundler/lightningcss#898)) ## Why? Global CSS selectors like View Transitions API require global scope to work properly. Currently, there's no clean way to use these selectors with CSS Modules in Next.js, which causes issues when: 1. Using the View Transitions API 2. Migrating from css-in-js libraries like styled-components to React Server Components with CSS Modules ## How? - Upgrades `postcss-modules-local-by-default` to 4.2.0 - Adds support for the `/* cssmodules-pure-no-check */` comment pattern in CSS files which disables CSS Modules localization for that file - This mirrors established escape hatches in other tools (`@ts-nocheck`, `/* eslint-disable */`) - Added tests (similar to one @samcx wrote previously) to ensure functionality works correctly The approach was previously approved by @samcx in issue #77232, who mentioned that LightningCSS was already bumped, but we needed to bump the PostCSS side Fixes #77232 --------- Co-authored-by: Sam Ko <[email protected]>
1 parent b6a66c7 commit ebc1150

File tree

12 files changed

+200
-13
lines changed

12 files changed

+200
-13
lines changed

packages/next/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
"picomatch": "4.0.1",
294294
"postcss-flexbugs-fixes": "5.0.2",
295295
"postcss-modules-extract-imports": "3.0.0",
296-
"postcss-modules-local-by-default": "4.0.4",
296+
"postcss-modules-local-by-default": "4.2.0",
297297
"postcss-modules-scope": "3.0.0",
298298
"postcss-modules-values": "4.0.0",
299299
"postcss-preset-env": "7.4.3",

packages/next/src/compiled/postcss-modules-local-by-default/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-lock.yaml

Lines changed: 5 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* cssmodules-pure-no-check */
2+
3+
/* View Transitions API - requires global scope to work properly */
4+
:global(::view-transition-old(root)) {
5+
animation-duration: 0.3s;
6+
}
7+
8+
/* a local class */
9+
.home {
10+
background-color: #f0f0f0;
11+
color: #333;
12+
font-size: 16px;
13+
padding: 20px;
14+
}
15+
16+
/* a global class */
17+
:global(.global) {
18+
font-weight: 700;
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { ReactNode } from 'react'
2+
3+
export default function RootLayout({
4+
children,
5+
}: Readonly<{
6+
children: ReactNode
7+
}>) {
8+
return (
9+
<html lang="en">
10+
<body>{children}</body>
11+
</html>
12+
)
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styles from './home.module.css'
2+
3+
export default function Home() {
4+
return (
5+
<div id="my-div" className={`${styles.home} global`}>
6+
<div>This text should be bold</div>
7+
</div>
8+
)
9+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import cheerio from 'cheerio'
3+
4+
describe('css-modules-pure-no-check', () => {
5+
const { isNextStart, next } = nextTestSetup({
6+
files: __dirname,
7+
})
8+
9+
it('should apply styles correctly', async () => {
10+
const browser = await next.browser('/')
11+
12+
const elementWithGlobalStyles = await browser
13+
.elementByCss('#my-div')
14+
.getComputedCss('font-weight')
15+
16+
expect(elementWithGlobalStyles).toBe('700')
17+
})
18+
19+
if (isNextStart) {
20+
it('should have emitted a CSS file', async () => {
21+
const html = await next.render('/')
22+
const $html = cheerio.load(html)
23+
24+
const cssLink = $html('link[rel="stylesheet"]')
25+
expect(cssLink.length).toBe(1)
26+
const cssHref = cssLink[0].attribs['href']
27+
28+
const res = await next.fetch(cssHref)
29+
const cssCode = await res.text()
30+
31+
expect(cssCode).toInclude(`.global{font-weight:700}`)
32+
expect(cssCode).toInclude(
33+
`::view-transition-old(root){animation-duration:.3s}`
34+
)
35+
})
36+
}
37+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { NextConfig } from 'next'
2+
3+
const nextConfig: NextConfig = {
4+
/* config options here */
5+
}
6+
7+
export default nextConfig
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* cssmodules-pure-no-check */
2+
3+
/* View Transitions API - requires global scope to work properly */
4+
:global(::view-transition-old(root)) {
5+
animation-duration: 0.3s;
6+
}
7+
8+
/* a local class */
9+
.home {
10+
background-color: #f0f0f0;
11+
color: #333;
12+
font-size: 16px;
13+
padding: 20px;
14+
}
15+
16+
/* a global class */
17+
:global(.global) {
18+
font-weight: 700;
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styles from './index.module.css'
2+
3+
export default function Home() {
4+
return (
5+
<div id="my-div" className={`${styles.home} global`}>
6+
<div>This text should be bold</div>
7+
</div>
8+
)
9+
}

0 commit comments

Comments
 (0)