Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workflow to use Tailwind in Vue components? #1

Closed
syropian opened this issue Nov 1, 2017 · 12 comments
Closed

Workflow to use Tailwind in Vue components? #1

syropian opened this issue Nov 1, 2017 · 12 comments

Comments

@syropian
Copy link

syropian commented Nov 1, 2017

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. 🎉

@reinink
Copy link
Member

reinink commented Nov 1, 2017

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?

@syropian
Copy link
Author

syropian commented Nov 1, 2017

Yep! Closing.

@syropian syropian closed this as completed Nov 1, 2017
@unxsist
Copy link

unxsist commented Nov 3, 2017

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.

@LannyBose
Copy link

@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?

@syropian
Copy link
Author

syropian commented Nov 9, 2017

@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: #150

@adamwathan
Copy link
Member

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.

@alexsasharegan
Copy link

alexsasharegan commented Feb 28, 2018

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.

@r0skar
Copy link

r0skar commented Feb 28, 2018

@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;.

@alexsasharegan
Copy link

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.

@wishinghand
Copy link

@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.

@Wurielle
Copy link

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>

@stephenkoo
Copy link

stephenkoo commented Dec 27, 2018

@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

DCzajkowski pushed a commit to DCzajkowski/tailwindcss that referenced this issue Jul 23, 2019
Update functions-and-directives.blade.md
DCzajkowski pushed a commit to DCzajkowski/tailwindcss that referenced this issue Jul 23, 2019
RobinMalfait added a commit that referenced this issue May 17, 2021
RobinMalfait added a commit that referenced this issue May 17, 2021
RobinMalfait added a commit that referenced this issue Nov 25, 2022
* WIP

* WIP

* remove warnings, everything builds now, tests are re-enabled

* cleanup + use FxHashSet

* remove timing related code

* sort candidates to guarantee consistent order

* update tests to reflect candidate sorting

* sort candidates

This is useful to do in Rust because we can do it in parallel. In Node
we will still sort, but since most things should be sorted already it
means that we don't have to spend too much time there.

Doing this, resulted in ~4ms -> ~0.9ms for sorting in Node.

* use String::from_utf8_unchecked because we already guaranteed valid utf8 when parsing

This gave us a ~10% speedup

* drop withAlpha on `theme`

* only sort for Jest tests

* Try to get a consistent criterion benchmark

* WIP

* WIP

* WIP: batching + FxHashSet

* WIP

* use fastest algorithm so far

* update input fixture with new changes from Tailwind UI

I'll make sure to get proper fixtures in the benchmark folder soon so
that we don't have to deal with this anymore.

* add 1000 generated fixture files

The idea behind these fixtures files is that they are generated based on
the classNames used in our Tailwind UI project without "leaking"
Tailwind UI.

- The fixtures take random class combinations from Tailwind UI and mix
  the class attribute before it was generated.
- The structure of the HTML is also generated.
- For text I've used a UUID.
- We can probably find a way to generate something good on the fly
  without checking these in but it will do for now.
- We can also probably improve them by making them even more "real" in
  the future (by replacing the UUIDs with real data for example)

Checking them in so that we all run the same benchmarks against the same
code.

* use checked in fixtures for benchmarks

* use the middle X files to benchmark with

* refactor

* Pull in tracing

* Work on better candidate parser

* wip

* WIP

* cleanup

* cargo clippy --fix

* cleanup unused dependencies in the CLI

* remove "simple" from benchmark

What the frick does simple even mean?

* Add test case for Ruby arrays

`w[foo]` isn’t a valid candidate part anyway (but @[foo] is) so we can pick up the internal bits as a candidate. Not 100% sure if we want to do this but it seems sensible-ish

* rename NAPI project

* Simplify

* bump @napi-rs/cli

Co-authored-by: Amos Wenger <[email protected]>
Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Mar 9, 2024
This will reduce the amount of candidates to handle. They would
eventually be skipped anyway, but now we don't even have to re-parse
(and hit a cache) at all.
RobinMalfait added a commit that referenced this issue Mar 21, 2024
* swap engines

* remove all oxide related files

* drop swap engine step from CI

* drop oxide tests where we read from a `.oxide.*` file

* drop swap-engines.js file

* drop unused `oxide` variable

Let's make eslint happy!
RobinMalfait added a commit that referenced this issue Sep 11, 2024
RobinMalfait added a commit that referenced this issue Sep 11, 2024
RobinMalfait added a commit that referenced this issue Sep 17, 2024
RobinMalfait added a commit that referenced this issue Sep 17, 2024
RobinMalfait added a commit that referenced this issue Sep 20, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 20, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 21, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 23, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 23, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 23, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
RobinMalfait added a commit that referenced this issue Sep 24, 2024
A bit of a vague commit message, but this does a lot of things. I could
split it up, but not sure if it's worth it. Instead, let's talk about
it.

While working on keeping track of comment locations I was running into
some issues. Not the end of the world, but we could make things better.

Paired with Jordan on this to rework the algorithm. The idea is that we
now do multiple passes which is technically slower, but now we can work
on separate units of work.

- Step #1 is to prepare the at-rule. This means that rules with multiple
  selectors will be split in multiple nodes with the their own single
  selector.
- Step #2 is to collect all the classes we want to create an `@utility`
  for.
- Step #3 is to create a clone of the main `@layer utilities` for all
  the non-`@utility` leftover nodes (E.g.: rules with element and ID
  selectors).
- Step #4 is to create a clone of the main `@layer utilities` node for
  every single `@utility <name>` we want to create.
- Step #5 is to go over every clone, and eliminate everything that is
  not part of the `@utility` in question. So we can remove siblings
  (except for comments near it) and go up the chain.
- Step #6 is now to go over the initial `@layer utilities` clone we set
  aside, and remove everything that's not part of any of the clones.
- Step #7 is cleanup work, where empty nodes are removed, and rules with
  a selector of `&` are replaced by its children. This is done in a
  depth-first traversal instead of breadth first.

Co-authored-by: Jordan Pittman <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants