Skip to content

Commit

Permalink
feat: support CSS side-effect imports (#4919)
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish authored Jan 4, 2023
1 parent 4805dd7 commit 26f33ee
Show file tree
Hide file tree
Showing 18 changed files with 835 additions and 46 deletions.
8 changes: 8 additions & 0 deletions .changeset/hungry-owls-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@remix-run/dev": minor
"@remix-run/react": minor
"@remix-run/server-runtime": minor
"@remix-run/testing": minor
---

Add unstable support for CSS side-effect imports via `future.unstable_cssSideEffectImports` feature flag
3 changes: 2 additions & 1 deletion docs/file-conventions/remix-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ The `serverBuildTarget` can be one of the following:

## serverDependenciesToBundle

A list of regex patterns that determines if a module is transpiled and included in the server bundle. This can be useful when consuming ESM only packages in a CJS build.
A list of regex patterns that determines if a module is transpiled and included in the server bundle. This can be useful when consuming ESM only packages in a CJS build, or when consuming packages with [CSS side-effect imports][css-side-effect-imports].

For example, the `unified` ecosystem is all ESM-only. Let's also say we're using a `@sindresorhus/slugify` which is ESM-only as well. Here's how you would be able to consume those packages in a CJS app without having to use dynamic imports:

Expand Down Expand Up @@ -186,3 +186,4 @@ There are a few conventions that Remix uses you should be aware of.
[an-awesome-visualization]: https://remix-routing-demo.netlify.app
[remix-dev]: ../other-api/dev#remix-dev
[app-directory]: #appDirectory
[css-side-effect-imports]: ../guides/styling#css-side-effect-imports
76 changes: 61 additions & 15 deletions docs/guides/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,23 +766,13 @@ Other CSS-in-JS libraries will have a similar setup. If you've got a CSS framewo

NOTE: You may run into hydration warnings when using Styled Components. Hopefully [this issue][styled-components-issue] will be fixed soon.

## CSS Modules
## CSS Bundling

<docs-warning>This feature is unstable and currently only available behind a feature flag. We're confident in the use cases it solves but the API and implementation may change in the future.</docs-warning>
<docs-warning>CSS bundling features are unstable and currently only available behind feature flags. We're confident in the use cases they solve but the API and implementation may change in the future.</docs-warning>
To enable [CSS Modules], set the `future.unstable_cssModules` feature flag in `remix.config.js`.
Many common approaches to CSS within the React community are only possible when bundling CSS, meaning that the CSS files you write during development are collected into a separate bundle as part of the build process.
```js filename=remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
future: {
unstable_cssModules: true,
},
// ...
};
```
With this feature flag enabled, the Remix compiler will now generate a single CSS file containing all CSS Modules styles in your application. Note that any [regular stylesheet imports][regular-stylesheet-imports] will remain as separate files.
When using CSS bundling features, the Remix compiler will generate a single CSS file containing all bundled styles in your application. Note that any [regular stylesheet imports](#regular-stylesheets) will remain as separate files.
Unlike many other tools in the React ecosystem, we do not insert the CSS bundle into the page automatically. Instead, we ensure that you always have control over the link tags on your page. This lets you decide where the CSS file is loaded relative to other stylesheets in your app.
Expand All @@ -806,7 +796,27 @@ export const links: LinksFunction = () => {
};
```
You're all set! You can now opt into CSS Modules via the `.module.css` file name convention. For example:
With this link tag inserted into the page, you're now ready to start using the various CSS bundling features built into Remix.

### CSS Modules

<docs-warning>This feature is unstable and currently only available behind a feature flag. We're confident in the use cases it solves but the API and implementation may change in the future.</docs-warning>
First, ensure you've set up [CSS bundling](#css-bundling) in your application.

Then, to enable [CSS Modules], set the `future.unstable_cssModules` feature flag in `remix.config.js`.

```js filename=remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
future: {
unstable_cssModules: true,
},
// ...
};
```

With this feature flag enabled, you can now opt into CSS Modules via the `.module.css` file name convention. For example:

```css filename=app/components/button/styles.module.css
.root {
Expand All @@ -833,6 +843,41 @@ export const Button = React.forwardRef(
Button.displayName = "Button";
```

### CSS Side-Effect Imports

<docs-warning>This feature is unstable and currently only available behind a feature flag. We're confident in the use cases it solves but the API and implementation may change in the future.</docs-warning>
Some NPM packages use side-effect imports of plain CSS files (e.g. `import "./styles.css"`) to declare the CSS dependencies of JavaScript files. If you want to consume one of these packages, first ensure you've set up [CSS bundling](#css-bundling) in your application.

Then, set the `future.unstable_cssSideEffectImports` feature flag in `remix.config.js`.

```js filename=remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
future: {
unstable_cssSideEffectImports: true,
},
// ...
};
```

Finally, since JavaScript runtimes don't support importing CSS in this way, you'll also need to add any relevant packages to the [`serverDependenciesToBundle`][server-dependencies-to-bundle] option in your `remix.config.js` file. This ensures that any CSS imports are compiled out of your code before running it on the server. For example, to use React Spectrum:

```js filename=remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
serverDependenciesToBundle: [
/^@adobe\/react-spectrum/,
/^@react-spectrum/,
/^@spectrum-icons/,
],
future: {
unstable_cssSideEffectImports: true,
},
// ...
};
```

[custom-properties]: https://developer.mozilla.org/en-US/docs/Web/CSS/--*
[link]: ../components/link
[route-module-links]: ../route/links
Expand All @@ -843,3 +888,4 @@ Button.displayName = "Button";
[tailwind-intelli-sense-extension]: https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss
[css modules]: https://github.com/css-modules/css-modules
[regular-stylesheet-imports]: #regular-stylesheets
[server-dependencies-to-bundle]: ../file-conventions/remix-config#serverdependenciestobundle
3 changes: 3 additions & 0 deletions integration/css-modules-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ test.describe("CSS Modules", () => {
"remix.config.js": js`
module.exports = {
future: {
// Enable all CSS future flags to
// ensure features don't clash
unstable_cssModules: true,
unstable_cssSideEffectImports: true,
},
};
`,
Expand Down
Loading

0 comments on commit 26f33ee

Please sign in to comment.