posthtml / posthtml-css-modules

Use CSS modules in HTML
MIT License
54 stars 5 forks source link

a sample about use posthtml-css-modules in webpack? #2

Open vagusX opened 8 years ago

vagusX commented 8 years ago

Looking forward to see it

maltsev commented 8 years ago

I'll add an example this weekend. Meanwhile, you can check the webpack section of posthtml.

maltsev commented 8 years ago

Actually, I have never worked with Webpack. So my example could be not the best one.

Install all needed modules:

npm install webpack posthtml posthtml-loader posthtml-css-modules

Create webpack.config.js:

module.exports = {
    entry: "./index.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            {
                test: /\.html$/,
                loader: 'html!posthtml'
            },
        ]
    },

    posthtml: function () {
        return {
            defaults: [require('posthtml-css-modules')('./cssClasses.json')]
        }
    }
}

Put HTML in the index.js:

document.body.innerHTML += '<h1 css-module="title">My profile</h1><div css-module="profile.user">John</div>';

Put CSS modules in the cssClasses.json:

{
  "title": "_title_116zl_1 _heading_9dkf",
  "profile": {
    "user": "_profile_user_f93j"
  }
}

@vagusX, is this example helpful for your task?

izeau commented 8 years ago

Hey @maltsev, I’m thinking about this issue but I don’t think there is a way (as of now) to use your plugin with Webpack without significant drawbacks.

First of all, the JSON file containing the mappings (cssClasses.json) must be defined before the transformations occur. Then of course we can only specify one single mapping file, which kind of defeats the purpose of CSS modules, right?

I think an option would be to use ES6 proxies so we can generate posthtml environments on the fly, and use a .css file instead of a .json with your plugin. That way, we could do this:

const markup = require('posthtml?./myfile.css!./myfile.html');

But I don’t know how you'll be handling the current directory to resolve relative paths (like ./myfile.css). It also doesn’t resolve the “create a CSS file with generated names” issue.

Now I get this use case is a bit tricky (using Webpack) – maybe what I want would better be done in a specific Webpack plugin. Let me know what you think!

maltsev commented 8 years ago

I looked more deeply into Webpack and how it works. @izeau probably you're right, there is no good way to integrate it with my plugin. I'll think about fixing that or even writing a new plugin/loader for Webpack.

@izeau @vagusX could you please share your use cases? It'll be very helpful for me to know how you'd use my tool before I start changing something.

izeau commented 8 years ago

@maltsev Sure, the goal here is for Webpack to rewrite the HTML templates to apply the generated class names, but also to produce a valid CSS file from the CSS modules. We need a way, for each template file, to use different CSS modules and to add generated CSS to the pipeline.

maltsev commented 8 years ago

I created an example of using CSS Modules in plain HTML with webpack. It doesn't use PostHTML, only the standard webpack loaders. Does it fit your needs?

izeau commented 8 years ago

It indeed works, but the main reason for me to use PostHTML is to bake the class names inside the templates at compile time, not have them be interpolated at runtime. I’m actually surprised nobody tried to do this yet :sob:

maltsev commented 8 years ago

I was thinking about that too. But I don't know how to keep related CSS modular in that case. Do you have any ideas or even better examples?

izeau commented 8 years ago

What if we wrote the templates like this?

<!-- define which CSS files will be used -->
<css module="button" src="./button.css">
<css module="calendar" src="./calendar.css">

<!-- css="[module].[className]" -->
<button css="button.primary">Foo</button>
<div css="calendar.calendar">
  <div css="calendar.header"></div>
  <!-- etc. -->
</div>

Will become (with dummy class names):

<button class="button_primary">Foo</button>
<div class="calendar_calendar">
  <div class="calendar_header"></div>
</div>

Of course this means we also need to extract CSS files from the HTML templates, and output the generated CSS file somewhere.

maltsev commented 8 years ago

What downsides has the runtime interpolation?

For me your example seems more complicated than the standard require:

<form class="${require('./Form.css').form}">
    <input type="text">
    <button class="${require('./Form.css').submitButton}">Submit</button>
</form>

Okay, the syntax isn't so good, but I think we can add some sugar:

<form css-module="./Form.css:form">
    <input type="text">
    <button css-module="./Form.css:submitButton">Submit</button>
</form>