Skip to content

Add composable transform utilities #1272

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

Merged
merged 13 commits into from
Dec 24, 2019
Merged

Add composable transform utilities #1272

merged 13 commits into from
Dec 24, 2019

Conversation

adamwathan
Copy link
Member

@adamwathan adamwathan commented Dec 23, 2019

This PR adds a set of basic transform utilities to Tailwind.

It includes support for the following properties:

  • transform
  • transform-origin

...and the following transform functions:

  • scale
  • rotate
  • translateX
  • translateY

One of the biggest challenges with this feature is that CSS does not inherently support composable transforms — you can only specify one transform property that must include all of your transform functions in one list.

That sucks when you want to take a utility-based approach, because it would be nice to be able to add rotate-45 scale-110 to an element and have both transforms be applied.

This PR solves this problem using CSS custom properties.

First, we define a transform class that looks like this:

.transform {
  transform: scale(var(--transform-scale, 1))
             rotate(var(--transform-rotate, 0))
             translateX(var(--transform-translate-x, 0))
             translateY(var(--transform-translate-y, 0));
}

You can think of this as "enabling" the transform feature — it sets the transform property to accept four custom properties and provides a "no-op" default for each one.

To specify the transform-origin, use the origin-{side} utilities:

<div class="transform origin-top-left">...</div>

We include top, top-right, right, bottom-right, bottom, bottom-left, left, top-left, and center out of the box, and they can be configured using the transformOrigin property in your theme.

Then each transform function has its own set of utilities that simply modify the custom property:

.rotate-45 {
  --transform-rotate: 45deg;
}

By doing things this way, you can combine multiple transforms on an element like this:

<div class="transform rotate-45 scale-110 translate-x-4 -translate-y-6">...</div>

translate uses our spacing scale right now, and I will be updating it to include percentages soon as well (at least 50% and 100%). It can be configured using the translate key in your theme.

rotate includes 0deg, 45deg, 90deg, and 180deg (plus the negative versions) out of the box, and can be configured using the rotate key in your theme.

scale includes 0, 0.5, 0.75, 0.9, 0.95, 1, 1.05, 1.1, 1.25, and 1.5. It can be customized using the scale key in your theme.

I've opted to make these functions separate theme keys because eventually all browsers will support CSS Individual Transforms for these three properties, so in a couple of years we can migrate to those without breaking the theme config.

What's not included

3D Transforms

This PR intentionally excludes support for 3D transform related stuff, like backface-visibility, perspective, and the 3D transform functions. We can iterate on support for that later without any BC breaks.

Scaling x and y separately

We can add this without making it a breaking change separately if there is demand for it, but I audited many sites and found no usage of scaleX or scaleY outside of complex keyframe animations so I don't think it will be missed.

Complex rotations

This PR only supports rotate(), not rotateX or rotateY. If there is enough demand for this we can add it later without a BC break.

Skew

This can be added later if there is demand, but I found essentially no usage of this in the wild aside from Stripe. It is challenging to come up with reasonable default values for skew because it's usually used in a very brand-specific way rather than to solve general purpose problems.

IE 11 support

IE 11 doesn't support CSS custom properties so this approach can't work there. Rather than cripple this feature in really frustrating ways, I've decided to forgo IE 11 support. If someone needs to support IE 11 and wants to use transforms in their project, they will have to do it with their own custom utilities.

Try it out

Here's a Tailwind.run where I've implemented this in user land so you can play with it:

https://tailwind.run/hpLzYj/8 (Updated 2019-12-23 7:35pm)

Let me know what you think of what I've done here! I don't want to bike-shed over this stuff too much since it's all configurable anyways, but if there are any obvious problems with how I've put this together it would be great to find out before I merge it in.

@reinink
Copy link
Member

reinink commented Dec 23, 2019

Beautiful. 🤟

@miked1ck
Copy link

miked1ck commented Dec 23, 2019

Love this. Any thought on supporting hover:*?

// Example
<div class="transform scale-100 hover:scale-110">...</div>

@elpete
Copy link

elpete commented Dec 23, 2019

Love this. Any thought on supporting hover:transform-*?

It works if you add hover in the config.variants.

@llaski
Copy link

llaski commented Dec 23, 2019

Awesome sauce

@tomhermans
Copy link

Looks like the sensible approach. Was reading this and also immediately thought of custom properties.

@georges-gomes
Copy link

It's nice, but the use of CCP means that children elements will also inherit the transform if they have the "transform" class.
This is a departure from anything else in Tailwind right?

@tlgreg
Copy link

tlgreg commented Dec 23, 2019

@georges-gomes I rarely nest transformed elements deeply honestly, but I can see it being an issue.

Maybe your original design @adamwathan might be better, resetting the properties on each .transform.

@georges-gomes
Copy link

In straight HTML I agree with you. But if a light-dom framework is used, you nested tons of components and you don't even realize they do use transforms.

I think it's easy to translate a nested scaled element, specially when you want to animate transitions or hover. The result will be unexpected.
My 2 cents

@adamwathan
Copy link
Member Author

Good call @tlgreg I’ll switch back to that approach, will fix the transform inheritance 👍🏻

@brandonpittman
Copy link

I’m thinking of elements that grow in one direction on hover, or something like a progress bar. Scaling x and y separately are needed for cases like that, and that’s why I included them in my transitions plugin.

@adamwathan
Copy link
Member Author

Just pushed an update that fixes the inheritance issue and also adds negative values for translate (originally left out by mistake) as well as 50% and 100%, since those are super common.

@brandonpittman any chance you can point me to a site you've seen that uses scaleX/Y for that purpose? What you're saying makes total sense and it would be helpful for me to see some real world examples to make sure I get the approach right.

@brandonpittman
Copy link

@adamwathan Forgive me for using a site I worked on as an example, but the nav links on this page have an :after element with scaleY(0). On hover, they transition to scaleY(1). Sure, you could do the same thing with overflow-hidden and translateY. But scaleY is more representative of the effect I want. Using just scale could get a weird effect where it's also growing out of the center as well.

https://centrip-japan.com/mission-traveler/

@brandonpittman
Copy link

As for IE11 support, I think the Postcss Custom Variables plugin will at least make sure that whatever variable is defined at build time will work as a default value.

@brandonpittman
Copy link

@adamwathan In my own transistions plugin, I used tx, -tx, ty, and -ty for translate shorthand (similar to existing margin and padding utilities). How to you feel about using something like that?

@adamwathan
Copy link
Member Author

Updated to support scale-x-{amount} and scale-y-{amount} utilities, just for you @brandonpittman 👍

In my own transistions plugin, I used tx, -tx, ty, and -ty for translate shorthand (similar to existing margin and padding utilities). How to you feel about using something like that?

I think I'd rather be a bit more verbose for this one since translate isn't as commonly used as margin/padding 👍

@adamwathan
Copy link
Member Author

@miked1ck

Love this. Any thought on supporting hover:*?

This is already enabled (as is focus) for all three of the supported transforms in this PR 👍

@brandonpittman
Copy link

@adamwathan That’s a nice little Christmas present! Thanks.

@adamwathan
Copy link
Member Author

Merging 😘

@adamwathan adamwathan merged commit 927d011 into v2 Dec 24, 2019
@adamwathan adamwathan deleted the transform-utilities branch December 24, 2019 12:30
@kevinruscoe
Copy link
Contributor

Very clever solution. I'm thinking this method could be used on my images to stack up different images/gradients.

@sasafister
Copy link

sasafister commented Jan 3, 2020

I like this approach. Transform vs. animation will be the killer feature

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.