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

"Circular dependency detected" error on using @apply in custom class #6386

Closed
dcastil opened this issue Dec 11, 2021 · 10 comments
Closed

"Circular dependency detected" error on using @apply in custom class #6386

dcastil opened this issue Dec 11, 2021 · 10 comments

Comments

@dcastil
Copy link
Contributor

dcastil commented Dec 11, 2021

What version of Tailwind CSS are you using?

v3.0.1

What build tool (or framework if it abstracts the build tool) are you using?

Tailwind Playground

What version of Node.js are you using?

Not applicable

What browser are you using?

Chrome Version 96.0.4664.55

What operating system are you using?

macOS 12.0.1

Reproduction URL

https://play.tailwindcss.com/oslkj6dpmz?file=css

Only change was adding this CSS

.group-inner:focus-within .group-inner-focus-within\:sr-only {
    @apply sr-only;
}

Describe your issue

I'm getting a compilation error "<css input>: Circular dependency detected when using: @apply sr-only". But I'd expect the project to run without issues. This class was used in Tailwind CSS v2 without any issues.

I also don't understand how the circular dependency could come up since I'm using a class name not available in Tailwind CSS: group-inner-focus-within:sr-only. The issue goes away when I remove the last character y from the class. Seems like Tailwind tries to evaluate the class as a Tailwind class despite the prefix not existing in Tailwind.

@danr-za
Copy link

danr-za commented Dec 13, 2021

Same here.
It seems to happen when applying a class inside a class with a same name.

@dcastil
Copy link
Contributor Author

dcastil commented Dec 17, 2021

I upgraded to v3.0.7 in my project with the circular dependency and the error doesn't come up again. Seems like the issue was fixed in #6588. Closing this issue as resolved, thanks @RobinMalfait! 🙏

@dcastil dcastil closed this as completed Dec 17, 2021
@RobinMalfait
Copy link
Contributor

@dcastil Ah perfect! Thanks for notifying and closing the issue!

@driskull
Copy link

driskull commented Dec 17, 2021

I'm still seeing this issue in 3.0.7.

You cannot `@apply` the `sticky` utility here because it creates a circular dependency.
header .sticky {
  @apply sticky;
}

https://play.tailwindcss.com/YxcQz4gSop?file=css

In our web components, we have a class .sticky where we use @apply sticky;. This seems to throw this error.

If we rename our class to .sticky-pos then it will work but we shouldn't have to do that.

@driskull
Copy link

I can open a new issue if you want.

@RobinMalfait
Copy link
Contributor

RobinMalfait commented Dec 18, 2021

This PR fixed false positives, but the example you posted is not a false positive but an actual circular dependency. It's like you would do this:

.btn {
  @apply btn;
}

@driskull
Copy link

Ok thanks. I'll update our classes. 👍

@vd-eerkul
Copy link

Hi @RobinMalfait

I also face similar, but in my case it's so weird.

For example this does not give any error.

.region > .block {
  @apply m-0;
}

But this does;

.sidr .block {
  @apply mb-0 h-auto;
}

even i do not use block inside that selector.

But what i found we also use block in other classes which might be related

.sidr ul.sf-menu.menu {
  @apply block bg-white shadow-utc;
}

The error is: You cannot `@apply` the `block` utility here because it creates a circular dependency.

Are there anything to escape utility?

@RobinMalfait
Copy link
Contributor

@vd-eerkul the issue is not the individual rules, they all work on their own. But it's when you use both of them at the same time.

It works if you only have this:

.region > .block {
  @apply m-0;
}

It also works if you only have this:

.sidr .block {
  @apply mb-0 h-auto;
}

It also works if you only have this:

.sidr ul.sf-menu.menu {
  @apply block bg-white;
}

But if you have this:

.sidr .block {
  @apply mb-0 h-auto;
}

.sidr ul.sf-menu.menu {
  @apply block bg-white;
}

then it won't work.

The reason for this is because by using .sidr .block, you extended the functionality of .block. Later you are using .block in an @apply, but we have to apply all rules for that class to ensure that you can use it as a drop-in replacement.

Later if you use @apply block inside the .sidr selector, then you have to apply all selectors that use .block, which includes the .sidr .block selector, but this already contains .sidr, so now you are applying .sidr inside of .sidr which is causing the infinite loop.

I know this sounds very confusing, but let's look at another example to try and understand what I mean with "applying all rules such that it works as a drop-in replacement".

Let's say you have this structure:

<div class="flex hover:underline"></div>

The CSS for this looks like:

.flex {
  display: flex;
}
.hover\:underline:hover {
  text-decoration-line: underline;
}

If you want to use apply to abstract it to a new class we have to make sure that:

  1. The element is a flex container (coming from flex)
  2. On hover, you add an underline (coming from hover:underline)
.foo { @apply flex hover:underline }

This generates:

.foo {
  display: flex;
}
.foo:hover {
  text-decoration-line: underline;
}

such that you can use this in your HTML as:

<div class="foo"></div>

This now applies all the rules, which means that you can safely use foo instead of flex hover:underline as a drop-in replacement and everything works exactly the same.

But now imagine that you wrote custom CSS like this as well:

.abc .flex { 
  color: red;
}

A bit farfetched maybe, but imagine you did. This means that if you were using:

<div class="abc">
  <div class="flex hover:underline">Hello World!</div>
</div>

That the text would be red if it is inside of an element with a .abc class. You now extended the behavior of the flex utility.

When using @apply we want to guarantee that it's a drop-in replacement. Which means that if you use the exact CSS as before:

.abc .flex { 
  color: red;
}
.foo { @apply flex hover:underline }

And later you use it in your html:

<div class="abc">
  <div class="foo">Hello World!</div>
</div>

Then we want the text to be red as well, so we have to take the .abc .foo class into account. This now generates the following CSS:

.abc .flex {
  color: red;
}
.foo {
  display: flex;
}
.abc .foo {
  color: red;
}
.foo:hover {
  text-decoration-line: underline;
}

Notice how the .abc .flex class also exists as .abc .foo.

I know this is a hard feature to wrap your head around, but we do have to apply all the rules to ensure that you can:

  1. Take classes from an element, e.g.: flex hover:underline
  2. Put it in another class and use @apply, e.g.: .foo { @apply flex hover:underline }
  3. Replace the original element's classes with the .foo class

If we circle back to your issue, you are extending the .block class by using it in a selector, e.g.: .sidr .block
You are later using @apply block inside another rule that has a selector of .sidr
Which now means you are using all rules of .sidr inside of .sidr which creates the circular dependency.

So, to solve this I would recommend to either:

  1. Not use @apply at all
  2. If you use @apply, do not use classes for existing utilities, and if you do, don't use these utilities in another @apply to prevent the circular dependency issue.

@vd-eerkul
Copy link

Thank you @RobinMalfait for the detailed answer.

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

5 participants