Tailwindcss: Workflow to use Tailwind in Vue components?

Created on 1 Nov 2017  ·  12Comments  ·  Source: tailwindlabs/tailwindcss

Ah yes, the dreaded first issue! Fortunately more of a question/discussion point than anything.

Is there a best way to use Tailwind with Vue single file components? It would be really nice to keep those one-off styles or component styles inside Vue components themselves, but the tricky part is getting component CSS between the preflight and utilities lines.

The only thing I can think of is to have 2 files — The first file would just contain @tailwind preflight;, and the second would have @tailwind utilities; + any custom utilities users want. In Laravel Mix, if we use the extractVueStyles option, that _should_ extract the Vue component styles at the top of the second file, which is what we want. We could then use Mix's combine() function to join 'em together.

Last I suppose we'd have to add Tailwind to vue-loader's PostCSS options.

Nevermind, all PostCSS plugins in Mix apply to Vue component styles as well, no need to do it manually. 👍

I haven't had a chance to actually test this approach but I figured I'd gather some thoughts in case there's a better way.

Edit Again: Ok! I tested the above approach and it seems to work great. My Vue styles get injected in the right spot, and I'm also able to successfully use Tailwind functions (config, @apply, etc) inside my Vue components. 🎉

Most helpful comment

So I can think of a few ways that we could make it possible to @apply classes that aren't defined in the same scope:

  1. Add some sort of @silent { ... } at-rule for generating the utilities so they are available when @apply runs but stripped by the time Tailwind is totally finished:

    <style>
    @silent {
        @tailwind utilities;
    }
    
    .whatever {
        @apply bg-red;
    }
    </style>
    
  2. Make Tailwind always fallback to looking for a matching utility in the utilities it would generate, even if they haven't been generated into the current scope. So @apply .bg-red would work as long as Tailwind would generate that utility if you did add @tailwind utilities to that file.

    The only thing I dislike about this approach is it doesn't let you use @apply for custom utilities that aren't generated by Tailwind like you can normally.

I'm somewhat hesitant to solve this problem because I really don't want to encourage people to start using Tailwind solely as a library for building component classes, but if it's really a big problem for people then maybe we can try something.

All 12 comments

Amazing! Thanks for testing this Collin. Very cool that it just works, but I guess that sort of makes sense. Are we good to close this issue then?

Yep! Closing.

Hi @syropian,

Can you show me your mix setup? I'm struggling with setting it up the way you suggested. My extracted Vue styles are just being inserted in between the output of preflight and utilities, but they're not being processed by PostCSS.

@syropian and @reinink, I'm hitting the same wall as @unxsist... Keep getting unable to find .bg-blue when trying to @apply in me Vue component... What's the mix config that could get me home here?

@unxsist @lperiodbose Unfortunately you have to include @tailwind utilities; in your style block. The very unfortunate side effect being this duplicates all of Tailwind into the output and your CSS file will row exponentially. More on this issue can be found here: https://github.com/tailwindcss/tailwindcss/issues/150

So I can think of a few ways that we could make it possible to @apply classes that aren't defined in the same scope:

  1. Add some sort of @silent { ... } at-rule for generating the utilities so they are available when @apply runs but stripped by the time Tailwind is totally finished:

    <style>
    @silent {
        @tailwind utilities;
    }
    
    .whatever {
        @apply bg-red;
    }
    </style>
    
  2. Make Tailwind always fallback to looking for a matching utility in the utilities it would generate, even if they haven't been generated into the current scope. So @apply .bg-red would work as long as Tailwind would generate that utility if you did add @tailwind utilities to that file.

    The only thing I dislike about this approach is it doesn't let you use @apply for custom utilities that aren't generated by Tailwind like you can normally.

I'm somewhat hesitant to solve this problem because I really don't want to encourage people to start using Tailwind solely as a library for building component classes, but if it's really a big problem for people then maybe we can try something.

Is there a solution for this that Vue users not using Mix could implement? It looks like @silent is not implemented. I'm just setting up tailwind in a large Vue project, and I honestly don't know yet how I'll need/want to compose with @apply, but it's advertised in the documentation by virtue of the Extracting Components section. Usage in a single file component environment shouldn't preclude me from being able to compose things in that environment, right? ~Especially if I'm going to use tailwind as the primary source of truth for a color palette.~ Possibly confusing my setup is the fact that SCSS is already implemented, so style blocks in vue are using that lang attribute.

Update
Looks like the failures to compile the @apply rules were breaking the color config usage. Color config works fine once all errors are fixed.

@alexsasharegan FYI you can use the config helper like this:

<style lang="postcss" scoped>
  html {
    background-color: config('colors.white');
  }
</style>

without including @tailwind utilities; in the Vue component. However, I have not been able to make @apply work without including @tailwind utilities;.

Thanks @r0skar, I updated my comment when I figured out that the broken @apply was also breakingconfig(). That's working now that I've removed all my attempts at @apply.

@syropian
I'm not sure I follow your solution. I'm using Laravel Mix and Vue single file components.

I split my main SASS file into two: preflight with just @tailwind preflight in it and utilities with @tailwind utilities and an import for custom classes covering what Tailwind can't do.

In my webpack.mix.js file it looks like this:

mix.js('resources/assets/js/app.js', 'public/js')
  .sass('resources/assets/sass/preflight.sass', 'public/css/app.css')
  .sass('resources/assets/sass/utilities.sass', 'public/css/app.css')
  .options({
    processCssUrls: false,
    postCss: [tailwindcss('./tailwind.js')],
    extractVueStyles: true
  });

This isn't working. extractVueStyles and Mix's .combine() method are undocumented so I'm not sure how to get them to work.

Since Tailwind 0.6.2 you can use shadowLookup to apply classes that aren't defined but would exist if @tailwind utilities was included in the same CSS file. See the release notes.

However, this feature is disabled by default behind flags. It is also stated that this features may be changed or removed at any time without any regard for semantic versioning.

If you still wish to enable it, add this to your tailwind config file:

module.exports = {
  // ...
  experiments: {
    shadowLookup: true
  }
}

You can then use your stylesheet like you normally would:

// ./src/App.vue generated by Vue-CLI 3
// ...
<style lang="scss">
#app {
  background-color: config('colors.yellow'); // eww
  * {
    color: config('colors.white');
  }
  @apply bg-black; // ahh better!
}
</style>

@Wurielle Am I doing something wrong? What happens is styles @apply w-full; gets compiled as css (as a string).

webpack.mix.js

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css')
    .options({
        processCssUrls: false,
        postCss: [tailwindcss('./tailwind.js')],
        hmrOptions: {
            host: '127.0.0.1',
            port: 8080,
        },
    });

mix.webpackConfig({
    module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.(js|vue)$/,
                loader: 'eslint-loader',
                exclude: /node_modules/,
            },
        ],
    },
});

tailwind.js

module.exports = {
    // ...
    options: {
        prefix: '',
        important: false,
        separator: ':',
        shadowLookup: true, // Should be official feature now
    },
    experiments: {
        shadowLookup: true, // Added just in case
    },
};

component.vue

<style lang="scss" scoped>
.cart-bg {
  @apply bg-black; // Just compiles into a string in css
}

.progress-bar {
  &::-webkit-progress-bar {
    @apply w-full; // Just compiles into a string in css
  }
}
</style>

UPDATE: Fixed by updating to unreleased laravel-mix version 4.0.12 which addresses this problem

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manniL picture manniL  ·  3Comments

paulhuisman picture paulhuisman  ·  3Comments

jbardnz picture jbardnz  ·  3Comments

spyric picture spyric  ·  3Comments

ghost picture ghost  ·  3Comments