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})`);
}