Here are the core concepts which I feel are important:
- Must be able to use composes from anywhere in the project.
- Have individual JSON files to load per each module.
- Be able to use duplicate class names across multiple modules with separate scope.
I already had grunt-postcss up and running on my project so I decided to stick with what is already working. I found a handy postcss-modules plugin which handles the class conversions to unique keys and builds JSON files.
This leaves me with a package.json file which looks like this
You may notice a strange key “css-modules-loader-core”: “https://github.com/lipemat/css-modules-loader-core.git”. I ended up forking the loader-core module because I couldn’t get the original to resolve while using “composes”. This is a known problem that is not getting much traction so I decided it would be more stable to fork and self maintain. Also adding the “resolutions” key prevents other libraries from getting other versions which break module location when using Yarn.
Out of the box, this plugin generates a single JSON file for you entire tree and does not account for duplicate class names. Not an ideal results so I headed over to my postcss config and made some adjustments.
This main part that we care about is here:
This allows the postcss-modules to work inside the import processes generating separate JSON files for each module and giving us unique selectors for duplicates. Also if you notice I have configured “globalModulePaths” which point to existing postcss styles for non modular elements. Anything within the specified directories is treated as normal non modular postcss and will generate standard css. It is particularly handy when your working with a framework like WordPress which has many classes hardcoded. globalModulePaths uses regular expression to check against the pcss file paths. In this example anything in a resources/pcss folder will be considered non modular. I included expressions for both Windows and Mac.
At this point we can start adding .pcss files to same directories we have our PHP templates located. We can then add a standard postcss @import to the bottom of our main .pcss file and grunt will start generating the unique identifiers and JSON files.
For instance, lets say I have a main pcss file called style.pcss which lives in my original pcss dir. I would add these lines.
I already have a template-parts directory which lives in the same directory as pcss and contains a test.php file. I’m now going to create the test.pcss file to match the test.php file. Running grunt will include the new test.pcss file in my main styles and generate a test.pcss.json file.
My template-parts directory now looks like this:
Everything is good to go as far as postcss and grunt are concerned but we still don’t have a way to actually use the styles. I went ahead and built a handy class to be used for this.
As you can see there is not much to it. This class is designed to work with WordPress so if your not running as such you will want to remove the trailingslashit() call. I suggest making the class a singleton and/or part of a DI container so you can instantiate it once and reused it everywhere. It is use like so:
- You construct it with the path of your templates
- Call styles() while passing the module you desire
- Use the array keys that match your class
Here is an example of the test.pcss file
Here is what the test.php could look like. Again if you construct and call set_path() somewhere globally you won’t have to construct the class at the top of each file.
When the html is generated it will automatically have the hashed class which matches the generated CSS. It will look something like this.
There you have it. You can now use the full functionality of css modules inside your PHP templates.