astroturfcss / astroturf

Better Styling through Compiling: CSS-in-JS for those that want it all.
https://astroturfcss.github.io/astroturf/
MIT License
2.28k stars 60 forks source link

Are BEM-style class names possible? #68

Closed SilentImp closed 5 years ago

SilentImp commented 5 years ago

Hi. I want to get classes like Card and Card--large. In the documentation I found an example like this:

const Card = styled('form')`
width: 150px;
height: 120px;
&.large {
  width: 300px;
  height: 240px;
}
`;
// …
return (<Card large />);

It will generate somethins like this:

<form class="UserCard-Card--3mYhX UserCard-Card--2O_am"></form>

with webpack config

    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            { 
              loader: "style-loader",
            },
            { 
              loader: "astroturf/css-loader",
              options: {
                modules: true,
                localIdentName: '[name]--[hash:base64:5]',
              },
            }
          ],
        },
        {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          use: ['babel-loader', 'astroturf/loader'],
        }
      ]
    },

But is there any way to get canonical BEM .Card and .Card--large classes?

<form class="Card__3mYhX Card--large__3mYhX"></form>

Regards. Anton.

taion commented 5 years ago

Yes – you need to configure localIdentName to calculate the name as you desire. See https://github.com/webpack-contrib/css-loader#localidentname.

SilentImp commented 5 years ago

@taion Actually, no, sir. I think you got the problem wrong. How I should set modifier class?

const Card = styled('form')`
width: 150px;
&--large {
  width: 300px;
}
`;
// …
return (<Card --large />);

Just wouldn't work. As you may see in the config example — I am aware of localIdentName property. But if I am wrong and I may somehow configure it to get what i need — please, show me how.

Regards.

SilentImp commented 5 years ago

@taion you would reopen issue or I should create a new one with the same content?

taion commented 5 years ago

You still do &.large in the CSS template string. You instead set getLocalIdentName to emit the CSS class name that you want – something like [name]--[local], but with a carve-out for the "root" class.

SilentImp commented 5 years ago

@taion If I will use localIdentName: '[name]--[local]', and component Card was used in the class UserForm component I will get: UserForm-Card--card UserForm-Card--large. I have created gist to show full class code: https://gist.github.com/SilentImp/a3969a4f24387b548c2a4891164f24d9

But anyway even if this, maybe, will do on trivial cases but in more complex — I don't think it would work. For example I have products list. Inside of this list I will have entities that both product and product list item. Usually this will look like this:

<ul class="productList">
  <li class="productList__item product">
    <b class="product__title">Item</b>
  </li>
  <li class="productList__item product product--discount">
    <b class="product__title">Item</b>
  </li>
</ul>

This is closest I can get: https://gist.github.com/SilentImp/e05ffa5aeb273e222ebcf849d3e577b1 It looks like it's just impossible. Maybe there are some ways to work with css more freely with such approach?

One another point: I shouldn't control class naming with webpack config. Event this project have chapter «Use without webpack». Bundler (and building process at all) should not be mixed with class naming.

I am clearly understand that with different tools you should use different approaches … but BEM naming is great approach to naming (including mixins and modifiers) and I don't want to throw it out if there are way to continue use it.

Regards.

jquense commented 5 years ago

I'm not sure I understand the value of trying to use BEM here. The whole idea of cases modules is that you don't need to care what the names are because they are all unique. This feels like trying to format compiled code.

If you like the BEM style naming conventions you can name your classes along those conventions using the css tag, for the styled use case tho I'm not sure there is much value in making the output markup pretty when sourcemaps are a thing

taion commented 5 years ago

also, as noted in the docs i linked above, you can use a getLocalIdent function to fully customize what you emit. as @jquense notes, you really shouldn't care what the emitted class names are, but to the extent you do, we're delegating this to the CSS loader, which gives you full control, as you would have if you were using CSS modules normally.

SilentImp commented 5 years ago

@jquense class name conventions have three main reasons to use them:

  1. Modules namespaces (more or less isolated) — and CSS modules with they're hashes solve this problem totally. Sometimes it's even causing problems like code duplication because of this.
  2. Debugging — in the development environment it's good to see readable class names at the generated source code. With BEM naming you usually can with no problem get the idea what's going on, what has gone wrong and where you may find a solution. All. Class. Element. It's a huge advantage.
  3. Code readability and maintaining. It helps a lot, from my experience, when you are trying to read code written by someone else.

So mainly it's about UX. hash from CSS modules is good enough for production.

If you like the BEM style naming conventions you can name your classes along those conventions using the css tag, for the styled use case tho I'm not sure there is much value in making the output markup pretty when source maps are a thing

Thanks, I'll give it a try and will return with feedback.

getLocalIdent

@taion Thanks, I will investigate it, but it, as I have mentioned earlier seems somehow wrong.

With all kind regards.

SilentImp commented 5 years ago

@jquense css`` with localIdentName: '[local]', is totally the solution. Thanks.