tailwindcss
tailwindcss copied to clipboard
Improve sorting of utilities that contain the same properties but one has more declarations than the other
This PR improves our sorting algorithm to better handle situations where two utilities have the same set of unique properties but one has more total declarations than the other.
This is motivated by trying to better sort what were considered "component classes" in v3, because in v4 we're hoping to just call all of those things "utilities" and stick them in the same layer, relying on heuristic-based sorting to keep things working the same way they did for people in v3.
Say we had a custom class that was similar to the container class from v3:
@utility max-w-responsive {
@media (min-width: 640px) {
max-width: 640px;
}
@media (min-width: 768px) {
max-width: 768px;
}
@media (min-width: 1024px) {
max-width: 1024px;
}
@media (min-width: 1280px) {
max-width: 1280px;
}
@media (min-width: 1536px) {
max-width: 1536px;
}
}
If you use max-w-responsive and max-w-md on the same element, right now max-w-responsive would take precedence over max-w-md because they both only target the max-width property, so our current sorting algorithm falls back to sorting alphabetically based on the actual class name.
This PR adds one more check before alphabetical sorting, which is checking how many instances exist of each unique property in nodes being sorted.
So since max-w-responsive contains 5 max-width declarations and max-w-md only contains one, the sorting algorithm considers max-w-md to be a more "specific" utility than max-w-responsive and ensures that max-w-md takes precedence.
I'm opening this as a draft for now because I'm still not totally sure this is necessary — the actual container class in v3 looks like this:
.container {
width: 100%;
@media (min-width: 640px) {
max-width: 640px;
}
@media (min-width: 768px) {
max-width: 768px;
}
@media (min-width: 1024px) {
max-width: 1024px;
}
@media (min-width: 1280px) {
max-width: 1280px;
}
@media (min-width: 1536px) {
max-width: 1536px;
}
}
…so it already includes two properties (notice width is there), which means it'll already be sorted before the max-w-* utilities.
I'm also a bit nervous that this unique property counting approach is flawed when looking at things that use @supports with fallbacks.
For example of you had a p-safe utility that looked like this:
@utility p-safe {
padding: auto;
@supports (padding: env(safe-area-inset-top, 20px)) {
padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px) env(safe-area-inset-bottom, 20px) env(safe-area-inset-left, 20px);
}
}
…that technically sets padding twice but it's not really the same as the max-w-responsive example because it's not trying to do more than one thing, it's just that it's declaring a fallback.
So I dunno, I think this is a pretty complicated one to think through and I'm kinda hoping we just don't need to think about it at all. But we already prototyped it last week, so I'm opening this as a point of discussion in case anyone has a clearer sense than me for whether we actually need to worry about this or not.