Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic CSS in CSS Modules when coming from Styled Components

The following question came up when I wanted to migrate from Styled Components to CSS Modules.

Let's say I have the following styled component which accepts a dynamic parameter offset and a dynamic CSS string theme:

const Li = styled.li`
  &.selected {
    background-color: grey;
  }

  margin-left: ${({ offset }) => offset}px;

  ${({ theme }) => theme};
`;

In my code, I'd use it the following way:

const Parent = () => (
  <List>
    {list.map((item) => (
      <Item
        key={item.id}
        id={item.id}
        selectedIds={[]}
        offset={24}
        theme={`
          &.selected {
            background-color: green;
          }
        `}
      >
        {item.name}
      </Item>
    ))}
  </List>
);

const Item = ({ id, offset = 0, theme, children }) => {
  return (
    <Li
      theme={theme}
      offset={offset}
      className={selectedIds.includes(id) && 'selected'}
    >
      {children}
    </Li>
  );
};

Requirement: Now I would really keep the Item's component API: passing a number offset and a style string theme. So essentially everything in Parent component should stay this way.

How can I convert the Item component internally to use CSS Modules instead of the styled Li component?

like image 269
Robin Wieruch Avatar asked Jan 28 '21 19:01

Robin Wieruch


2 Answers

It's probably a different way of thinking than you used to but it can work

  1. You can use css variable
style={{ [`--offset`]: `${offset}px` }}
.item {
  margin-left: var(--offset);
}
  1. You can have a css module (file) dedicated to themes. In your case, it has withSelected
.withSelected {
  &.selected {
    background-color: green;
  }
}

So you could pass it as "theme"

theme={themes.withSelected}

This is how the components look

import styles from "./style.module.scss";
import themes from "./themes.module.scss";

const Parent = () => (
  <ul>
    {list.map((item) => (
      <Item
        key={item.id}
        id={item.id}
        selectedIds={[1]}
        offset={24}
        theme={themes.withSelected}
      >
        {item.name}
      </Item>
    ))}
  </ul>
);

const Item = ({ id, offset = 0, theme, children, selectedIds }) => {
  return (
    <li
      className={`${styles.item} ${theme} ${
        selectedIds.includes(id) && themes.selected
      }`}
      theme={theme}
      style={{ [`--offset`]: `${offset}px` }}
    >
      {children}
    </li>
  );
};

Demo: https://codesandbox.io/s/styledcomponent-to-css-modules-1kbqx

like image 107
Mosh Feu Avatar answered Oct 19 '22 14:10

Mosh Feu


With 1 I'd concur with @Mosh to just use the style prop. Css modules are static by design and there no way to get this done otherwise (I think that styled-components also uses the style prop so you're not losing anything).

For 2 you can leverage Sass modules which allow you to define your theme in a single place and import them as required:

/theme/_colors.scss

$red: rgb(200, 0 50);

/components/Item.module.scss

@import "../theme/colors"

.selected {
    background: $red;
}

Note: if you use Create React App absolute paths you can import from root as ~theme/colors

like image 27
Mordechai Avatar answered Oct 19 '22 15:10

Mordechai