Hugo pipes, postCSS & postcss-import - file system context, part 2



this question relates to Pipes, postCSS & postcss-import - how to keep filesystem context and somewhat to Regenerating assets directory for Hugo Pipes.

There seems to be a problem with path context while using Hugo pipes with postCSS.

I created a Tailwindcss-starter theme repo (see github) to speed up the development of themes with the use of Tailwindcss. (Thanks @budparr for the direction)

Here is the problem I’m chewing:

If I clone the theme repo as a seperate project with the folder structure:

  ├ assets/
  │ └ css/
  │   ├ dev/
  │   │  └ postcss.config.js
  │   ├ postcss.config.js
  │   ├ site.css
  │   ├ styles.css
  │   └ tailwind.js
  └ exampleSite
      └ content

and start it from within the theme repo folder using the exampleSite directory for the content with:

hugo server -s exampleSite --themesDir=../.. -w --disableFastRender

everything works fine. postCSS and postcss-import handle the imports as expected.

However, if I clone the repo as a theme into the themes directory of a new Hugo site with the following folder structure:

  ├ content/
  ├ ... // some other folders
  └ themes/
    └ my-theme/
      ├ assets/
        └ css/
          ├ dev/
          │  └ postcss.config.js
          ├ postcss.config.js
          ├ site.css
          ├ styles.css
          └ tailwind.js

and start the project with:

hugo server -v

I receive the following error:

INFO 2019/01/13 19:48:52 postcss: use config file /Users/user/my-site/themes/my-theme/assets/css/dev/postcss.config.js
Error: Specified Tailwind config file "/Users/user/my-site/assets/css/tailwind.js" doesn't exist.
    at exports.default (/Users/user/my-site/themes/my-theme/node_modules/tailwindcss/lib/lib/registerConfigAsDependency.js:9:11)
    at _postcss2.default.plugin.config (/Users/user/my-site/themes/my-theme/node_modules/tailwindcss/lib/index.js:37:59)
    at creator (/Users/user/my-site/themes/my-theme/node_modules/tailwindcss/node_modules/postcss/lib/postcss.js:150:35)
    at Object.<anonymous> (/Users/user/my-site/themes/my-theme/assets/css/dev/postcss.config.js:16:31)
    at Module._compile (internal/modules/cjs/loader.js:721:30)
    at requireFromString (/usr/local/lib/node_modules/postcss-cli/node_modules/require-from-string/index.js:28:4)
    at parseJsFile (/usr/local/lib/node_modules/postcss-cli/node_modules/cosmiconfig/dist/loadJs.js:15:15)
ERROR 2019/01/13 19:48:54 error: failed to transform resource: exit status 1
Total in 1096 ms
Error: Error building site: logged 1 error(s)

The postcss.config.js is successfully found. It consist of the following setup:

module.exports = {
    plugins: [
            path: ['./assets/css'] // where to look for the css assets
        require('tailwindcss')( './assets/css/tailwind.js'), // where to find the tailwind.js file
            browsers: ['>1%']

But the path to tailwind.js from within postcss.config.js is not found. It seems like postCSS is loosing the context, that it was called from within the themes folder and uses instead the my-site root path.

There is a workaround, which is a bit ugly as it breaks the relative paths in styles.css:

// postcss.config.js
const themeDir = __dirname + '/../../..'; // this translates to /Users/user/my-site/themes/my-theme/

module.exports = {
    plugins: [
            path: [themeDir]
        require('tailwindcss')(themeDir + '/assets/css/tailwind.js'),
            browsers: ['>1%']

But now I have to change the import paths in styles.js to:

/* Tailwind Base - Variables: tailwind-config.js */
@import "node_modules/tailwindcss/preflight";
/* Tailwind component classes registered by plugins*/
@import "node_modules/tailwindcss/components";
/* Site Specific */
@import "assets/css/site";
/* Tailwind's utility classes - generated based on config file */
@import "node_modules/tailwindcss/utilities";

Any hint on how to make postCSS make remember the file system context?


Okay, I think I traced it back to a Hugo core function in hugo/resources/resource_transformers/postcss/postcss.go:

func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {

    const localPostCSSPath = "node_modules/postcss-cli/bin/"
    const binaryName = "postcss"

    // Try first in the project's node_modules.
    csiBinPath := filepath.Join(, localPostCSSPath, binaryName)

    binary := csiBinPath

    if _, err := exec.LookPath(binary); err != nil {
        // Try PATH
        binary = binaryName
        if _, err := exec.LookPath(binary); err != nil {
            // This may be on a CI server etc. Will fall back to pre-built assets.
            return herrors.ErrFeatureNotAvailable

    // rest of file left out

Here Hugo tries the projects path and then the global PATH.

Maybe this function should be refactored to first try the projects theme path (if a theme is used), than the projects root path and after that the global PATH.


As I updated my starter report to use Tailwindcss v1.0.1, the error with context aware paths still exists. Does anyone maybe has a hint for me? Help is greatly appreciated.


What about working around the issue by configuring tailwind from within postcss.config.js?

      theme: {
        extend: {}
      variants: {},
      plugins: []

Interesting…, but no!

The Tailwindcss configuration should stay separate and not entangled with another any configuration file.

It is actually not an issue with the configuration of Tailwindcss. It is an issue with the lookup order from where PostCSS is started and where it looks for the /node_modules folder.

If I start a new site and start Hugo server from root, it should look first for /node_modules under the specific theme, and after that under the sites root /node_modules. Fallback is the global /node_modules folder.

  ├ content/
  ├ ... // some other folders
  ├ node_modules // site's node_modules (if any)  <-- look here second
  └ themes/
    └ my-theme/
      ├ assets/
      ├ ... // some other theme content folders
      └ node_modules // <-- look here first

Instead Hugo does not look into the themes /node_modules folder at all, and so does not find the tailwind package.