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

JIT: Optimize universal selector usage by inlining only the relevant selectors #4850

Merged
merged 13 commits into from Jul 1, 2021

Conversation

adamwathan
Copy link
Member

@adamwathan adamwathan commented Jul 1, 2021

Currently (especially in JIT) we make heavy use of the universal selector (*) to do these sort of "resets" for composable utilities, transform for example:

*, ::before, ::after {
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}

It turns out reading/writing to CSS variables can be expensive on site that have a lot of DOM nodes (like the Tailwind CSS home page) and that causes things to render more slowly than they really should have to if you only apply those rules when needed.

This PR adds a separate optimization step that ensures what used to be the universal selector is replaced with only the selectors needed, so the output looks more like:

.rotate-3,
.scale-x-150,
.hover\:scale-110,
.focus:rotate-6,
.md\:translate-x-4 {
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
}

...where the selectors included are all of the ones used in your template files that depend on this reset.

To make this work, we've added a new @defaults at-rule, which we consider private API for now until we're sure it's the correct abstraction.

@RobinMalfait
Copy link
Contributor

RobinMalfait commented Jul 1, 2021

bitmoji

@adamwathan adamwathan merged commit fe27356 into master Jul 1, 2021
3 checks passed
@adamwathan adamwathan deleted the optimize-universal-separate-pass branch Jul 1, 2021
@kirillrogovoy
Copy link

kirillrogovoy commented Aug 7, 2021

Hi!

Is there a Github issue about the performance problems this PR intended to solve?

2.2.5 has saved our lives and we have pinned the version because this change gets rolled back consequently. I wanted to describe our case in the issue but I failed to find any.

@gregonarash
Copy link

gregonarash commented Sep 2, 2021

Hi,

Using TW 2.2.9 and JIT.

We are building mobile first website (ReactJS, TS, NextJS, FramerMotion) and we are always testing performance on iPhone5( as lower end benchmark). There are about 700-800 dom elements on the main page.

I was attempting to add Tailwind to the project and it resulted in very choppy performance iPhone 5 - loosing frames in animation, content disappearing while scrolling. On newer phones it is less noticeable but there is a slowdown.

After 2 days of breaking everything down I found out that @tailwind utilities; was making all the difference, now reading this tread I see that only the universal selector from utilities is making the difference.

I have substituted @tailwind utilities; with the compiled CSS and narrowed it down, that cutting below code, makes visible impact on performance.

*, :after, :before { --tw-translate-x: 0; --tw-translate-y: 0; --tw-rotate: 0; --tw-skew-x: 0; --tw-skew-y: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); --tw-border-opacity: 1; border-color: rgba(229, 231, 235, var(--tw-border-opacity)); --tw-blur: var(--tw-empty, /*!*/ /*!*/); --tw-brightness: var(--tw-empty, /*!*/ /*!*/); --tw-contrast: var(--tw-empty, /*!*/ /*!*/); --tw-grayscale: var(--tw-empty, /*!*/ /*!*/); --tw-hue-rotate: var(--tw-empty, /*!*/ /*!*/); --tw-invert: var(--tw-empty, /*!*/ /*!*/); --tw-saturate: var(--tw-empty, /*!*/ /*!*/); --tw-sepia: var(--tw-empty, /*!*/ /*!*/); --tw-drop-shadow: var(--tw-empty, /*!*/ /*!*/); --tw-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); }

I hope it helps!

@gregonarash
Copy link

gregonarash commented Sep 5, 2021

@adamwathan, I noticed that you have rolled back this fix. Will you be adding it again at a later stage?

@gregonarash
Copy link

gregonarash commented Sep 9, 2021

@adamwathan I have just tested v2.2.10 with the experimental flag

  experimental: {
    optimizeUniversalDefaults: true,
  },

Our website speed (perceived fluidity of scrolling and animations) with above flag is back to stage before adding Tailwind.

No side effects noticed.

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

Successfully merging this pull request may close these issues.

None yet

4 participants