๐บ๏ธ CSS Modules #4631
Replies: 9 comments 12 replies
-
Love this. One thing to consider with generalizing Can you foresee any potential issues with bundling output from CSS Modules and Vanilla Extract into the same stylesheet? What about other solutions that generate atomic CSS instead of scoped selectors (a la Stylify or [one day] Stylex) where the cascade might be more relevant? That's probably out-of-scope here, but supporting it in the future could require a breaking change with a generalized virtual module. |
Beta Was this translation helpful? Give feedback.
-
For the declare module "*.css" {
let asset: string;
export default asset;
// extend the existing *.css definition to include a named export
export const styles: Record<string, string>;
} That way we can keep using the default import for the file's public path location. Obviously this isn't ideal as the Of course, if TS could simply disambiguate Either that or somehow generate Footnotes
|
Beta Was this translation helpful? Give feedback.
-
So exciting! I have a clarifying question about the longer-term outlook. Given this RFC is taking the path currently supported by esbuild, how do you imagine CSS Module and Vanilla Extract support will change with more robust support for these tools in esbuild? From their docs
I also have a more wacky question, if you'll humor me ๐
. It seems like esbuild supports bundling css based on entrypoints. Could Remix bundle CSS by treating each route component as a separate entrypoint? I suppose it wouldn't solve problems with duplicate styles loaded across routes but it would reduce |
Beta Was this translation helpful? Give feedback.
-
I've been experimenting a bit to see what the simplest CSS Modules implementation could look like (and also as a way to get more acquainted with Remix internals). Here's what I've got so far.
|
Beta Was this translation helpful? Give feedback.
-
@markdalgleish We should add a note to this RFC about handling |
Beta Was this translation helpful? Give feedback.
-
Based on my initial work and some internal discussion, I've changed the API for importing the bundled CSS: -import { bundledCssUrl } from '@remix-run/css';
+import cssBundleHref from '@remix-run/css-bundle'; Note that it's now a default export so you can easily name it whatever you like. I also made the package name a bit less vague. |
Beta Was this translation helpful? Give feedback.
-
There was an open question about how we can get types for Consumers may still want to generate better types than the default ( |
Beta Was this translation helpful? Give feedback.
-
I've updated the RFC to add an open question about whether or not we should support named exports from CSS Modules, e.g. |
Beta Was this translation helpful? Give feedback.
-
Hello friends! As @ryanflorence said, don't forget to pay attention to importing css files directly - I recently started building a library of React components and chose Vanilla Extract for styling. The motivation was to obtain components with their css files duly compiled and that did not depend on any other runtime task, being compatible with the main approaches of React 18+ applications - with their new features - and their most popular metaframeworks (Next 13, Remix, etc). I used Rollup and its vanilla-extract plugin, and built the components bundle independently, where each component loads its own CSS file - When testing in React apps (CRA or Vite) everything works perfectly, but when importing and using my components in remix routes I have no success. I've tried all the approaches related to my case presented on this page - https://remix.run/docs/en/1.14.3/guides/styling#css-bundling - But the best I could do was render my components without their respective css style. I don't have enough knowledge to collaborate in the development or in proposals for solutions, but I leave this need that I believe is not just mine. |
Beta Was this translation helpful? Give feedback.
-
CSS Modules
Prior art
Related issues
Summary
This proposal aims to add built-in support for CSS Modules via imports of
*.module.css
files, offered as an alternative to the existing supported styling solutions.Motivation
CSS Modules is a widely used community standard for scoping CSS that many frameworks/meta-frameworks support out of the box. A lot of developers who come to Remix are surprised to find that there's no built-in support. This makes it difficult for them to adopt Remix, either because they're trying to migrate an existing codebase/component library that uses CSS Modules, or it's their preferred way of writing maintainable CSS.
API and tradeoffs
CSS Modules are opt-in via the community standard file extension
*.module.css
. Any CSS file named this way will be treated as a CSS Module.In order to keep things simple and get to a working version sooner, especially since esbuild doesn't support code splitting of CSS due to concerns with CSS ordering, we're opting to output a single CSS file for all CSS Modules used in the app. This will need to be clearly noted within the Remix documentation since, while this is a suitable tradeoff for many applications, it obviously has issues at much larger scales where the generated file could be quite large. Anyone who is concerned about CSS bundle size should be recommended to look at alternative solutions, particularly those that create atomic CSS.
Based on @chaance's prior work in this space, Remix can provide access to the bundled CSS URL via a separate package which should then be included in your root route.
A notable difference to prior work is that this package needs to be generalised beyond CSS Modules (
'@remix-run/css-bundle'
rather than'@remix-run/css-modules'
) so that we can add support for other CSS bundling solutions in the future, e.g. importing CSS within node_modules, Vanilla Extract, CSS import side effects (e.g.import "./example.css"
).Open questions
What format should we use for scoped class names and should it differ between dev and prod? e.g. it's common to have debuggable class names during development, like
.Button-module__root__a4X9gs
, but only emit the hash in production, like.a4X9gs
.How should we handle named exports from CSS Modules, if at all?
For example, let's assume the following CSS:
It's expected that you can access these via the default export:
It may also be expected that you can access classes via named exports too. For example, the following is supported in Next.js:
However, this clashes with the proposed global type for
*.module.css
files since it can only describe the default export ({ [key: string]: string }
). This means that any attempt to access named exports (as in the example above) will be a type error. This can only be fixed by generating specific types for each CSS file. In fact, even Next.js reports a type error for this code when using TypeScript. IMO this means we probably shouldn't support named exports from CSS Modules.Another tradeoff with named exports is that you won't be able to access class names that are invalid JS identifiers, e.g. it's not valid to use hyphenated names like
import { example-hyphenated-class } from './example.module.css'
or use reserved works likeimport { break } from './example.module.css'
.Beta Was this translation helpful? Give feedback.
All reactions