Quantcast
Channel: SaltyCrane: javascript
Viewing all articles
Browse latest Browse all 33

Aphrodite to CSS Modules codemod

$
0
0

I wanted to convert our React project fromAphrodite toCSS Modules. The biggest impetus was that Aphrodite isn't supported by the newNext.js v13 app directory feature, which I'm excited to try. I like styled-components, but my co-worker likes CSS Modules and it's hard to go wrong with CSS Modules. CSS Modules also has built-in support in Next.js and it looks pretty good inthis graphic from the State of CSS survey.

To ease the conversion, I wrote ajscodeshift codemod to automate most of the process.The codemod is on github here:aphrodite-to-css-modules-codemod. An example is below.

The codemod worked well for my 200 Aphrodite files. I did spend time manually converting JS constants into CSS variables. I also manually handled CSS precedence issues since Aphrodite handles precedence more nicely than CSS. But overall I was pretty happy with the results. (It was certainly more successful than myattempt at a reactstrap-to-react-bootstrap codemod which I never used.)

Before

./example/src/MyComponent.tsx:

import { css, StyleSheet } from "aphrodite";
import classNames from "classnames";
import React from "react";

import { colors } from "./constants";
import { hexToRgbA } from "./utils";

export default function MyComponent() {
  const isSomething = true;
  const isSomethingElse = false;
  return (
    <div
      className={css(
        isSomethingElse ? myStyles.containerGrid : myStyles.containerFlex,
      )}
      style={{}}><div className={css(myStyles.header, myStyles.content)}>header</div><div className={classNames(css(myStyles.content), "another-class")}><div>Lorem ipsum</div></div><span className={css(isSomething && myStyles.warning)}></span></div>
  );
}

// comment I
export const myStyles = StyleSheet.create({
  containerGrid: {
    backgroundColor: "white",
    // comment 1
    /* comment 2 */ display: "grid" /* comment 4 */, // comment 5
    gridTemplate: `"sourceselect .       reviewbutton" auto"pagination   filters filters     " auto"rowcount     filters filters     " 20px
      / 2fr         1fr     2fr
    `,
    width: 200,
  },
  containerFlex: {
    display: "flex",
  },
  content: {
    lineHeight: 1.5,
  },
  header: {
    backgroundColor: "#ccc",
    color: hexToRgbA(colors.danger, 0.8),
    display: "inline-block",":hover": {
      color: colors.primary,
      borderColor: `${colors.info} !important`,
    },
  },
  // comment a
  warning: {
    fontWeight: 700,
    color: colors.warning,
    opacity: 0,
  } /* comment b */, // comment c
});

After

./example/src/MyComponent.tsx:

import myStyles from "./MyComponent.module.css";
import classNames from "classnames";
import React from "react";

import { colors } from "./constants";
import { hexToRgbA } from "./utils";

export default function MyComponent() {
  const isSomething = true;
  const isSomethingElse = false;
  return (
    <div
      className={
        isSomethingElse ? myStyles.containerGrid : myStyles.containerFlex
      }
      style={{}}><div
        className={
          // TODO: check CSS precedence
          classNames(myStyles.header, myStyles.content)
        }>
        header</div><div className={classNames(myStyles.content, "another-class")}><div>Lorem ipsum</div></div><span className={classNames(isSomething && myStyles.warning)}></span></div>
  );
}

export { myStyles };

./example/src/MyComponent.module.css:

/* comment I */
.containerGrid {
  background-color: white;
  /* comment 1 */
  /* comment 2 */
  display: grid; /* comment 4 */ /* comment 5 */
  grid-template: "sourceselect .       reviewbutton" auto"pagination   filters filters     " auto"rowcount     filters filters     " 20px
  / 2fr         1fr     2fr
;
  width: 200px;
}

.containerFlex {
  display: flex;
}

.content {
  line-height: 1.5;
}

.header {
  background-color: #ccc;
  color: var(--bs-danger-alpha80);
  display: inline-block;
}

.header:hover {
  color: var(--bs-primary);
  border-color: var(--bs-info) !important;
}

/* comment a */
.warning {
  font-weight: 700;
  color: var(--bs-warning);
  opacity: 0;
} /* comment b */ /* comment c */

JS context file

The expressions in the styles object (e.g. colors.danger,hexToRgbA(colors.danger, 0.8), etc.) were evaluated using the following "context" file.

./context.example.js:

const colors = {
  danger: "var(--bs-danger)",
  info: "var(--bs-info)",
  primary: "var(--bs-primary)",
  warning: "var(--bs-warning)",
};

function hexToRgbA(hex, alpha) {
  return hex.replace(/\)$/, `-alpha${alpha * 100})`);
}

Viewing all articles
Browse latest Browse all 33

Latest Images

Trending Articles





Latest Images