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

Remove need for transform toggle #4604

Merged
merged 1 commit into from
Jun 10, 2021
Merged

Remove need for transform toggle #4604

merged 1 commit into from
Jun 10, 2021

Conversation

adamwathan
Copy link
Member

@adamwathan adamwathan commented Jun 10, 2021

This PR makes all of the individual transform classes like scale-*, translate-x/y-*, skew-x/y-*, and rotate-* work without the need for using the transform class to "enable" them. This introduces minor breaking changes depending on how you were authoring your HTML, so for now this only targets the JIT engine where it's okay for us to make these sorts of changes.

<!-- Before -->
<div class="transform scale-50 translate-x-1/2 rotate-90">

<!-- After -->
<div class="scale-50 translate-x-1/2 rotate-90">

It also adds a new transform-cpu class for explicitly using CPU rendering instead of GPU if you need to change that responsively:

<div class="transform-gpu scale-50 translate-x-1/2 rotate-90 md:transform-cpu">

How it works

This works by adding a new rule targeting the universal (*) selector to the base layer that looks like this:

* {
  --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));
}

Then each individual transform-related class looks something like this:

.scale-50 {
  --tw-scale-x: 0.5;
  --tw-scale-y: 0.5;
  transform: var(--tw-transform);
}

.rotate-45 {
  --tw-rotate: 45deg;
  transform: var(--tw-transform);
}

Breaking changes

The primary breaking change here is that the transform utilities are now dependent on the @tailwind base layer because of where we inject the universal rule. If you are only using @tailwind utilities (probably because you don't want to use our reset styles), the transforms will stop working.

The solution is to make sure you do include @tailwind base, but explicitly disable our preflight styles if you don't want those:

  /* Your custom existing CSS here... */

+ @tailwind base;
  @tailwind utilities;
// tailwind.config.js
module.exports = {
  // ...
  corePlugins: {
    preflight: false,
  }
}

Aside from that, the main thing to understand is that there is no transform class anymore, so any authoring patterns that relied on being able to use that to enable transforms will break.

For example, if you were conditionally adding the transform class using JS while leaving classes like scale-50 applied permanently, that will no longer work:

{/* Won't work anymore */}
<div className={`scale-50 rotate-90 ${shouldTransform ? 'transform' : ''}`}>

Instead you would need to conditionally apply the actual transformations:

{/* Do this instead */}
<div className={`${shouldTransform ? 'scale-50 rotate-90' : ''}`}>

Another example is if you were using the transform class to manually switch from GPU rendering to CPU rendering at a specific breakpoint. I would be shocked if anyone is doing this in a real project in the wild:

<div class="transform-gpu scale-50 md:transform md:scale-50">

To do this now, you would write the HTML like this:

<div class="transform-gpu scale-50 md:transform-cpu">

Notice that you don't need to re-specify md:scale-50 like you would have historically.

@zensimilia
Copy link

What about browsers performance? Any benchmarks?

@adamwathan
Copy link
Member Author

@zensimilia Any recommendations on how to get those numbers? We actually already use this approach for shadows so we are already adding universal selectors for this sort of thing and no one has ever raised it as a performance issue in their projects.

* {
  --tw-shadow: 0 0 #0000
}

.shadow-sm {
  --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
}

My guess is that since all we are doing is setting variables and not applying any styles that it wouldn't be an issue but I agree it would be nice to prove that.

@zensimilia
Copy link

@adamwathan on my machine, when i add to * rule transform: scaleX(1) (Chrome DevTools https://tailwindcss.com/) i have some freezes with scrolling.

@adamwathan
Copy link
Member Author

@zensimilia Yeah the big difference though is you are applying transform to every element in that case, but in this PR we aren't applying any styles at all, only declaring variables.

/* Bad */
* {
  transform: scaleX(1)
}

/* I think this is okay */
* {
  --tw-transform: scaleX(1)
}

@neupauer
Copy link
Contributor

Hi 👋, I like this idea. I had a similar idea, but with a slightly different implementation.

The biggest concern I had was the performance but I haven't explored that idea further.

I'm just putting it here perhaps as inspiration for a different approach.

#3936

@ghost
Copy link

ghost commented Jun 10, 2021

How's that different from?

:root {
  --tw-transform: scaleX(1)
}

@adamwathan
Copy link
Member Author

How's that different from?

:root {
  --tw-transform: scaleX(1)
}

The main difference is that it's re-applied on every element to prevent inheritance. If you put that on :root, and had this HTML:

<div class="translate-x-1/2">
  <div class="font-bold">
    <div class="scale-50">
      <!-- ... -->

...the scale-50 element would also have translate-x-1/2 applied because the --tw-translate-x variable would never be set back to 0.

@adamwathan
Copy link
Member Author

Hi 👋, I like this idea. I had a similar idea, but with a slightly different implementation.

The biggest concern I had was the performance but I haven't explored that idea further.

I'm just putting it here perhaps as inspiration for a different approach.

#3936

Hey thanks for sharing, sorry I didn't notice that discussion thread before! Yeah this is a suggestion that has come up in the past and I know others have tried with some success too 👍

My main point of hesitation around that approach is potential false positives, like if someone is using Tailwind to layer utilities on top of some existing CSS and that pattern happens to match one of their existing styles that is doing something unrelated, it could cause problems.

Like if someone has a custom class from years in the past in their project called rotate-user-avatar or something, you know? So I've held off on trying to solve this until I had an idea that really felt bullet proof, and for some reason it just struck me yesterday, hah.

@adamwathan adamwathan merged commit b86aa5c into master Jun 10, 2021
@adamwathan adamwathan deleted the no-transform-toggle branch June 10, 2021 13:15
@ghost
Copy link

ghost commented Jun 10, 2021

Move fast and break things 😄
What about filter, and backdrop-filter, etc. One by one?

@adamwathan
Copy link
Member Author

On it 🙂

image

@kevchcm
Copy link

kevchcm commented Jul 12, 2021

@adamwathan I have version '2.2.4' but the translate doesn't seem to work without the transform class.

Also I'm trying to use a negative translate with a custom setting and doesn't work either

I have this extended on my theme

translate: {
    'screen' : '100vw'
}

So im trying to use

<div class="-translate-x-screen"></div>

But

  • Doesn't work with the negative
  • Doesn't work without the transform

So I had to do a workaround and do this on my theme

translate: {
    'screen' : '100vw',
    'screen-ng' : -'100vw'
}

and I have to use it like this

<div class="transform -translate-x-screen-ng"></div>

@neupauer
Copy link
Contributor

Hi @kevchcm 👋, you probably don't have Just-in-Time Mode mode enabled.

This feature is currently enabled only in JIT mode, otherwise you still have to use the toggle.

See: https://play.tailwindcss.com/OJ0LbfoE4f ( remove the mode: 'jit' in the Config to see the difference )

Screen Shot 2021-07-13 at 07 33 20

@kevchcm
Copy link

kevchcm commented Jul 13, 2021

@neupauer you're completely right, I don't have the jit mode, I'm sorry for that.

Does that answer the question for the negative values on translate?

Is it normal that I have to add a negative variant like this

translate: {
    'screen' : '100vw',
    'screen-ng' : -'100vw'
}

I read the documentation and it doesn't say anything like that

@neupauer
Copy link
Contributor

Hi @kevchcm, sorry I overlooked the second part of the question :).

Yes it is perfectly valid to add custom values for the specific needs of your design.

As you can see (the image below), the list of default values doesn't cover all possible values, so adding custom values is perfectly fine.

Screen Shot 2021-07-13 at 17 56 54


You can add the -100vw to your tailwind.config.js like this:

module.exports = {
  theme: {
    extend: {
      translate: {
        "-screen": "-100vw"
      }
    },
  },
}

Tailwind will generate utilities -translate-x-screen and -translate-y-screen.


If you have Just-in-Time Mode enabled, you can achieve this by using the arbitrary value translate-x-[***]

translate-x-[-100vw]

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.

4 participants