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

[v4] addComponents is adding styles to @layer utilities instead of @layer components #15045

Open
saadeghi opened this issue Nov 19, 2024 · 8 comments

Comments

@saadeghi
Copy link

What version of Tailwind CSS are you using?

v4.0.0-alpha.34

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

@tailwindcss/cli

Reproduction URL

https://github.com/saadeghi/tw4-component-layer-issue

Describe your issue

These are the layers in output CSS file:

@layer theme, base, components, utilities;

Expectation
It's expected for addComponents to add styles to @layer components

Current behavior
Currently addComponents adds styles to @layer utilities, similar to addUtilities

Plugin example:
https://github.com/saadeghi/tw4-component-layer-issue/blob/master/myplugin.js

Generated style:
https://github.com/saadeghi/tw4-component-layer-issue/blob/9b7a944690a35d55c7406756e30cc98c7a239623/output.css#L516

@RobinMalfait
Copy link
Member

Hey!

In v4 this is done on purpose. The biggest benefit of the split in v3 is that you can always override a component with more specific utilities. However, in v4 we improved sorting of all the utilities, which means that this rule should still apply but it reduces the amount of concepts in the system.

Can you share some of the issues you are seeing because of this?

@saadeghi
Copy link
Author

Thanks for the quick answer.

Sure.
The issue is order of styles which is important because order is important in CSS.
I updated the example: https://github.com/saadeghi/tw4-component-layer-issue

For example here I have 2 plugins:
https://github.com/saadeghi/tw4-component-layer-issue/blob/master/input.css

addComponents adds both of them to @layer utilities:
https://github.com/saadeghi/tw4-component-layer-issue/blob/064182b7d683bde9c9bfb9c618a334ed33136202/output.css#L512

But I have no control over the order:

@layer utilities {
  .bigcard {
    background: green;
    padding: 2rem;
  }
  .p-10 {
    padding: calc(var(--spacing) * 10);
  }
  .card {
    background: yellow;
  }
}

For example why one plugin came before p-10 and the other came after p-10?
How are they being sorted
Changing the order in input.css doesn't change anything.

@saadeghi
Copy link
Author

Scenario
What can I do to make sure .bigcard comes after .card? because I want it to modify the default .card style so it must come after .bigcard?
I tried using addUtilities for .bigcard but it works exactly like addComponents.

Workaround
I know I can use:

  • low specificity styles like :where()
  • CSS variables to modify styles in modifier class names instead of CSS rules

But these solutions increases the CSS size and complexity. So I hope there's a way I can control the styles for component (.card) always come before the modifier styles (.bigcard) and Tailwind utility classes.
Expected order:

/* component */
.card {
  background: yellow;
}
/* modifiers */
.bigcard {
  background: green;
  padding: 2rem;
}
/* utilities */
.p-10 {
  padding: calc(var(--spacing) * 10);
}

So the style for this HTML would be predictable

<div class="card">default</div>
<div class="card bigcard">bigcard style overrides the default</div>
<div class="card bg-red-500">bg-red-500 overrides the default</div>

@RobinMalfait
Copy link
Member

Yep I see, the order inside of your CSS is indeed very important. In v4 how it works is that we sort all the utilities (and components) based on the used properties in the CSS and the amount of properties you used.

The idea is that a component with multiple properties (which is typically the case) will be sorted before the ones with fewer properties. In your case .card has a single property and .bigcard has 2, so you can override the background color of .bigcard with .card.

The order difference between .card and .p-10 is based on the used properties. Since the properties used in those classes don't have any overlap, the order between those classes doesn't matter (note: the output is still deterministic and won't appear in random spots during rebuilds).

It also looks like the .card and .bigcard examples are made up to simplify the reproduction (which I appreciate!). But since sorting is based on the amount of properties and which properties were used, a more real example will help here.

Can you make a reproduction with a real example just to verify that you are still running into these issues?

@saadeghi
Copy link
Author

saadeghi commented Nov 20, 2024

sorting is based on the amount of properties and which properties were used

I see! Thanks for the info.
I have a lot of modifier classes, with less or more properties than the component class, and some override others but the problem was I didn't know why they are ordered this way and I thought putting the components in the @layer components would give me a predictable order.

👍 I think you can close this issue if there's no plan to use @layer components. I will check and modify my components to make sure the modifiers have less styles than the components.

The idea is that a component with multiple properties (which is typically the case) will be sorted before the ones with fewer properties

sorting is based on the amount of properties and which properties were used

Can you confirm If I understood the logic correctly:
If a class name has a property that overlaps with properties from another class name, the one with more properties comes first?

@vnphanquang
Copy link

vnphanquang commented Nov 21, 2024

@saadeghi I ran into similar problems while migrating to v4 and decided to use this pattern:

@layer utilities.components.base, utilities.components.modifier;

@utility btn {
  @layer components.base {
    /* base css for btn */
  }
}

@utility btn--outlined {
  @layer components.modifier {
    /* specific css for outlined */
  }
}

So for example, for this markup...

<button class="btn btn--outlined bg-blue-500">...</button>

...specificity is correctly observed: btn < btn--outlined < bg-blue-500, regardless of the ordering in output.

Verbose? Yes, but works well for my use cases. Similarly in JS syntax:

api.addUtilities({
  '.btn': {
    '@layer components.base': {
      // ...
    },
  },
})

Hope that's helpful.

@zoltanszogyenyi
Copy link

Related to this, I would add a new function that adds styles outside the TW layer structure:

#15074

Something like:

addCSS()

@stuyam
Copy link

stuyam commented Nov 22, 2024

Similar issues, when using the tailwind-forms plugin, they are now added to the utilities section, which means I also needed to move my form additions to the utilities layer now instead of the components layer that it was in in v3.

@layer utilities {
  /* Forms */
  .form-input,
  .form-textarea,
  .form-select,
  .form-multiselect,
  .form-checkbox,
  .form-radio{
    @apply block w-full rounded border-0 py-1.5 cursor-pointer
    text-gray-900 shadow-xs ring ring-inset ring-gray-200
    placeholder:text-gray-400 focus:ring-2 focus:ring-inset
    focus:ring-indigo-600 sm:text-sm sm:leading-6;
  }

The issue here is now if I want to do a rounded-r-none if I want to make no rounded corners on the right side to butt up against a button for example, I need to make some (but not all, seemingly random based on order) of the utils important like !rounded-r-none.

It feels like the form plugin would want to be added in the components layer, since they apply a lot of styles that you want to control and be able to override on top of inline.

Here is a working example of the issue: https://play.tailwindcss.com/kuXFgLDpPH

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