Skip to content

Instantly share code, notes, and snippets.

@chaance
Last active March 22, 2022 16:00
Show Gist options
  • Save chaance/6457cd138214c687e7ad2f63efbea609 to your computer and use it in GitHub Desktop.
Save chaance/6457cd138214c687e7ad2f63efbea609 to your computer and use it in GitHub Desktop.

Remix styling roadmap

Up to this point, Remix has taken a hands-off approach to styling with anything other than plain CSS. This made sense in the beginning because we took a very different approach to CSS than most other component-driven frameworks. We want users to actually understand how CSS actually makes it to the page, and following the path of other frameworks means introducing some magic that obscures that.

That being said, it's not a viable strategy long-term to stay agnostic about styles. Remix is a full-stack framework, and styling is a key part of the frontend. We need to support tooling to improve and simplify the user experience of authoring styles, and we should do it in a way that does not diverge from our core philosophy.

The main goal is eventual feature parity with other mainstream tools in the JS ecosystem (Create React App, Vite, Next) and full-stack frameworks (Laravel, RoR). This means support (or clean migration paths) for:

  • CSS Modules
  • PostCSS
  • Relative URL transformations
  • Sourcemaps
  • SCSS
  • Bare CSS imports

These are ordered by priority based on cascading difficulty.

CSS Modules

Currently in progress. The big remaining hurdles IMO are:

  • Resolving relative imports via @import or composes
    • This requires that we process all CSS modules syncronousely before the browser/server builds can continue, as all hashed selectors will need to be known and injected into our JS modules
  • Source maps
    • This is a bit trickier that it seems on the surface because our implementation writes the output to a single file vs. separate files or even separate inline <style> tags, which means line-numbers will be off. There are existing solutions in other bundlers we can look to that will hopefully make this pretty straight-forward.

We can release initial experimental support without resolving these for now. This will allow us to get feedback on our API and identify any pain points with migration while we work on this sub-feature.

/* ui/button.module.scss */
.button {
  composes: box from "./box.module.css";
  appearance: none;
  background-color: blue;
}

/* ui/box.module.scss */
.box {
  display: block;
}

/* result */
.button__x2jsj {
  -webkit-appearance: none;
  appearance: none;
  background-color: blue;
}

.box__s76k9 {
  display: block;
}
// ui/button.jsx
import styles from "~/ui/button.css";
let Button = () => <button className={styles.button} />;

// result
let Button = () => <button className="button__x2jsj box__s76k9" />;

PostCSS

Once CSS Modules are released, we'll already have a pre-processor in place to compile plain CSS as well. Should be relatively simple to introduce.

Under the hood we may use ParcelCSS instead. Devon has considered support for postcss.config.js where all PostCSS plugins that enable spec-compliant behavior would be ported to their compiler. This would trade 100% feature parity for a significantly faster build, and it would encourage folks to write more future-proof CSS where eventually the processing may no longer be necessary. We could write code-mods for commonly used non-spec PostCSS (such as SCSS-style nesting selectors).

Relative URL transformations

At the moment, url() functions with relative paths won't work as expected since they will resolve relative to the assets directory. We should transform these into absolute paths. We should be able to do this rather simply with our pre-processors.

/* ui/poop.jpg */
💩

/* ui/poop.css */
.poop {
  background-image: url("./poop.jpg");
}

/* result */
.poop {
  background-image: url("/_assets/poop__maybe-optimized-hash.jpg");
}

Sourcemaps

Already mentioned for CSS Modules, we will need this for all pre-processed CSS.

SCSS

SCSS is still a widely used pre-processor and is supported by basically every other framework we compete with. As we are already opening the box for processing styles, this should relatively simple to support. Essentially we just need to resolve the imports before processing the source and adding the output to our asset manifest.

Bare CSS imports

At the component level, these imports make little sense. Conflicts are likely and cascade ordering is a major footgun. If we ultimately decide to support this for compatibility + ease of migration, we should discourage its usage in our docs and explain the benefits of avoiding it. We very likely will not support bare imports, but rather provide a migration script that does one of the following:

  1. Convert .css to .module.css (same with .scss) and rewrite classname references
    • This could be tricky to implement in a bullet-proof way, but it could get them 80% of the way there
    • This is likely undesirable for some, as previously global selectors are now scoped; outside references to those selectors no longer work
  2. Parse all imported modules to map out and remove existing stylesheet imports, and generate remix-global.css with CSS @imports that can be imported in the root
    • This shouldn't change any behavior unless there are cascade issues, but now users have explicit control over the cascade and can quickly make necessary changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment