├── .gitignore
├── .release-it.json
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── index.js
├── package-lock.json
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "tagName": "v%s",
4 | "requireCleanWorkingDir": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [2.2.0] - 2020-02-03
9 |
10 | ### Added
11 | - Added a `default` transition property, which generates a simple `transition` class and includes all the properties from `colors` as well as `opacity`, `box-shadow`, and `transform` (taken from Tailwind 1.2)
12 | - Added a `shadow` transition property (taken from Tailwind 1.2)
13 | - Added `50`, `75`, `150`, `200`, `300`, and `400` transition durations
14 |
15 | ### Changed
16 | - The `transition-colors` utility now includes the `fill` and `stroke` properties, in addition to `background-color`, `border-color`, and `color` (taken from Tailwind 1.2)
17 |
18 | ## [2.1.0] - 2019-09-04
19 |
20 | ### Changed since 2.1.0-beta.2
21 | - Allowed for durations and delays to be defined as numbers but be output as `ms`
22 |
23 | ### Changed since 2.0.x
24 | - Changed the implementation of the `default` duration, timing function, and delay to prevent generating base styles that change the value of `transition-property`, `transition-duration`, `transition-timing-function`, and `transition-delay` on all elements, in order to prevent conflicts with libraries that don’t expect these properties to differ from their CSS-defined default
25 | - Base styles targeting all elements are still generated if a `default` duration, timing function, and/or delay is set (which is the case by default: the `default` duration is `250ms`), but they define custom properties that are very unlikely to conflict with third-party libraries (e.g. `--transition-duration`); these are then used to set actual properties on elements that have a `transition-[property]` or `transition-[duration]` class
26 | - As a result, it is now possible to only use a transition duration utility (e.g. `transition-500`) on an element to make it transition all its properties; no need for `transition-all` anymore, since the default value of `transition-property` is already `all`
27 |
28 | ## [2.1.0-beta.2] - 2019-09-01
29 |
30 | **Release `2.1.0-beta.1` was deprecated and reverted, so the following changes were made from release `2.0.1`:**
31 |
32 | ### Changed
33 | - Changed the implementation of the `default` duration, timing function, and delay to prevent generating base styles that change the value of `transition-property`, `transition-duration`, `transition-timing-function`, and `transition-delay` on all elements, in order to prevent conflicts with libraries that don’t expect these properties to differ from their CSS-defined default
34 | - Base styles targeting all elements are still generated if a `default` duration, timing function, and/or delay is set (which is the case by default: the `default` duration is `250ms`), but they define custom properties that are very unlikely to conflict with third-party libraries (e.g. `--transition-duration`); these are then used to set actual properties on elements that have a `transition-[property]` or `transition-[duration]` class
35 | - As a result, it is now possible to only use a transition duration utility (e.g. `transition-500`) on an element to make it transition all its properties; no need for `transition-all` anymore, since the default value of `transition-property` is already `all`
36 |
37 | ## [2.1.0-beta.1] - 2019-08-01 [DEPRECATED]
38 |
39 | **Decided to go in another direction for v2.1, and reverted most of the changes in this release.**
40 |
41 | ### Added
42 | - Added the ability to set a `default` transition property, which is set to `auto` by default, meaning that it depends on the `default` transition duration: if it’s `0s` or `0ms` then `auto` resolves to `all`, otherwise it resolves to `none`
43 |
44 | ### Changed
45 | - Changed the `default` transition duration from `250ms` to `0ms`
46 | - As a result of the above, base styles targeting all elements are no longer generated by default since the default property and duration are now the same as the CSS defaults (`all` and `0s`, respectively)
47 | - Therefore, with the new defaults you only need a transition duration utility (e.g. `transition-fast`) on an element to make it transition all its properties (no need for `transition-all`); if you want the previous behaviour, simply set the `default` transition duration to a non-zero value
48 | - Changed the default transition durations from a numeric scale (`250`, `500`, `750`, etc.) to a descriptive scale (`slow`, `medium`, `fast`, etc.)
49 | - Changed the default transition delays to match the new duration values, but with a numeric scale (`0`, `200`, `400`, `600`, `800`, and `1000`)
50 |
51 | ## [2.0.1] - 2019-05-16
52 |
53 | ### Changed
54 | - The base styles which set the default duration, timing function, and delay are now applied to pseudo-elements as well (changed the `*` selector to `*, *::before, *::after`)
55 |
56 | ## [2.0.0] - 2019-05-13
57 |
58 | ### Changed since 2.0.0-beta.2
59 | - Added support for global variants thanks to Tailwind’s `variants()` helper function
60 |
61 | ### Added since 1.x
62 | - Tailwind 1.0.0 compatibility
63 |
64 | ### Changed since 1.x
65 | - The plugin doesn’t accept a config object anymore; instead it finds what it needs in the `theme` and `variants` keys of your config (see `README` for more info)
66 | - Multiple utilities as well as responsive variants are now generated by default (see `README` for more info)
67 | - Shorter class names for transition durations (`transition-duration-500` is now `transition-500`) and timing functions (`transition-timing-ease` is now `transition-ease`)
68 | - Transition utilities now set `transition-property` instead of the `transition` shorthand; as a result, responsive transition utilities don’t reset the duration, timing function or delay anymore
69 | - For the above to work, the plugin now sets some base styles on all elements (changes `transition-property`’s default value of `all` to `none`, and sets the default duration, timing function, and delay defined in the theme)
70 |
71 | ## [2.0.0-beta.2] - 2019-04-07
72 |
73 | ### Removed
74 | - Removed `transitionProperty` and `willChange`’s `default` key feature introduced in beta 1; it is confusing because the behaviour is different from the other `default` keys in this plugin, but also because it’s not clear what the classes do... it’s better to be explicit (e.g. `transition-all` and `will-change-contents`)
75 |
76 | ## [2.0.0-beta.1] - 2019-04-07
77 |
78 | ### Added
79 | - Tailwind 1.0.0 compatibility
80 | - `transitionProperty` and `willChange` now accept a `default` key that generates a simple `transition` / `will-change` class
81 |
82 | ### Changed
83 | - The plugin doesn’t accept a config object anymore; instead it finds what it needs in the `theme` and `variants` keys of your config (see `README` for more info)
84 | - Multiple utilities as well as responsive variants are now generated by default (see `README` for more info)
85 | - Shorter class names for transition durations (`transition-duration-500` is now `transition-500`) and timing functions (`transition-timing-ease` is now `transition-ease`)
86 | - Transition utilities now set `transition-property` instead of the `transition` shorthand; as a result, responsive transition utilities don’t reset the duration, timing function or delay anymore
87 | - For the above to work, the plugin now sets some base styles on all elements (changes `transition-property`’s default value of `all` to `none`, and sets the default duration, timing function, and delay defined in the theme)
88 |
89 | ## [1.0.4] - 2018-11-04
90 |
91 | ### Added
92 | - Added proper tests with Jest
93 |
94 | ## [1.0.3] - 2018-11-04
95 |
96 | ### Added
97 | - Added a changelog and a release script
98 |
99 | ## [1.0.2] - 2018-11-04
100 |
101 | ### Fixed
102 | - Fixed escaping of transition delay selectors
103 |
104 | ## [1.0.1] - 2018-08-14
105 |
106 | ### Added
107 | - Added `timingFunctions` option
108 | - Added `delays` option
109 |
110 | ### Fixed
111 | - Fixed escaping in selectors generated by the plugin
112 |
113 | ## [1.0.0] - 2018-05-06
114 |
115 | Initial release
116 |
117 | [Unreleased]: https://github.com/benface/tailwindcss-transitions/compare/v2.2.0...HEAD
118 | [2.2.0]: https://github.com/benface/tailwindcss-transitions/compare/v2.1.0...v2.2.0
119 | [2.1.0]: https://github.com/benface/tailwindcss-transitions/compare/v2.1.0-beta.2...v2.1.0
120 | [2.1.0-beta.2]: https://github.com/benface/tailwindcss-transitions/compare/v2.0.1...v2.1.0-beta.2
121 | [2.1.0-beta.1]: https://github.com/benface/tailwindcss-transitions/compare/v2.0.1...v2.1.0-beta.1
122 | [2.0.1]: https://github.com/benface/tailwindcss-transitions/compare/v2.0.0...v2.0.1
123 | [2.0.0]: https://github.com/benface/tailwindcss-transitions/compare/v2.0.0-beta.2...v2.0.0
124 | [2.0.0-beta.2]: https://github.com/benface/tailwindcss-transitions/compare/v2.0.0-beta.1...v2.0.0-beta.2
125 | [2.0.0-beta.1]: https://github.com/benface/tailwindcss-transitions/compare/v1.0.4...v2.0.0-beta.1
126 | [1.0.4]: https://github.com/benface/tailwindcss-transitions/compare/v1.0.3...v1.0.4
127 | [1.0.3]: https://github.com/benface/tailwindcss-transitions/compare/v1.0.2...v1.0.3
128 | [1.0.2]: https://github.com/benface/tailwindcss-transitions/compare/v1.0.1...v1.0.2
129 | [1.0.1]: https://github.com/benface/tailwindcss-transitions/compare/v1.0.0...v1.0.1
130 | [1.0.0]: https://github.com/benface/tailwindcss-transitions/releases/tag/v1.0.0
131 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # ISC License
2 |
3 | Copyright (c) Benoît Rouleau
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⛔️ DEPRECATED
2 |
3 | Tailwind CSS 1.2 (released in February 2020) has utilities for transition [property](https://tailwindcss.com/docs/transition-property), [duration](https://tailwindcss.com/docs/transition-duration), and [timing function](https://tailwindcss.com/docs/transition-timing-function). It doesn’t have transition delay or `will-change` utilities (yet), nor does it have a way to define a default transition duration or timing function, but I feel like keeping this plugin around would be more confusing than helpful since it uses similar (or identical in some cases) class names as the core transition utilities. Feel free to fork it if you want, but I recommend migrating to Tailwind’s official solution and write custom utilities for `transition-delay` and `will-change` when needed.
4 |
5 | # Transitions Plugin for Tailwind CSS
6 |
7 | ## Installation
8 |
9 | ```bash
10 | npm install tailwindcss-transitions
11 | ```
12 |
13 | ## Usage
14 |
15 | ```js
16 | // tailwind.config.js
17 | module.exports = {
18 | theme: {
19 | transitionProperty: { // defaults to these values
20 | 'none': 'none',
21 | 'all': 'all',
22 | 'default': ['background-color', 'border-color', 'color', 'fill', 'stroke', 'opacity', 'box-shadow', 'transform'],
23 | 'colors': ['background-color', 'border-color', 'color', 'fill', 'stroke'],
24 | 'bg': 'background-color',
25 | 'border': 'border-color',
26 | 'color': 'color',
27 | 'opacity': 'opacity',
28 | 'shadow': 'box-shadow',
29 | 'transform': 'transform',
30 | },
31 | transitionDuration: { // defaults to these values
32 | 'default': '250ms',
33 | '0': '0ms',
34 | '50': '50ms',
35 | '75': '75ms',
36 | '100': '100ms',
37 | '150': '150ms',
38 | '200': '200ms',
39 | '250': '250ms',
40 | '300': '300ms',
41 | '400': '400ms',
42 | '500': '500ms',
43 | '750': '750ms',
44 | '1000': '1000ms',
45 | },
46 | transitionTimingFunction: { // defaults to these values
47 | 'default': 'ease',
48 | 'linear': 'linear',
49 | 'ease': 'ease',
50 | 'ease-in': 'ease-in',
51 | 'ease-out': 'ease-out',
52 | 'ease-in-out': 'ease-in-out',
53 | },
54 | transitionDelay: { // defaults to these values
55 | 'default': '0ms',
56 | '0': '0ms',
57 | '100': '100ms',
58 | '250': '250ms',
59 | '500': '500ms',
60 | '750': '750ms',
61 | '1000': '1000ms',
62 | },
63 | willChange: { // defaults to these values
64 | 'auto': 'auto',
65 | 'scroll': 'scroll-position',
66 | 'contents': 'contents',
67 | 'opacity': 'opacity',
68 | 'transform': 'transform',
69 | },
70 | },
71 | variants: { // all the following default to ['responsive']
72 | transitionProperty: ['responsive'],
73 | transitionDuration: ['responsive'],
74 | transitionTimingFunction: ['responsive'],
75 | transitionDelay: ['responsive'],
76 | willChange: ['responsive'],
77 | },
78 | plugins: [
79 | require('tailwindcss-transitions')(),
80 | ],
81 | };
82 | ```
83 |
84 | The default configuration generates the following CSS:
85 |
86 | ```css
87 | /* base styles for the default transition duration, timing function, and delay (when they differ from the CSS defaults) */
88 | *, *::before, *::after {
89 | --transition-duration: 250ms;
90 | /* when the default timing function is a value other than "ease": */
91 | --transition-timing-function: [default-timing-function];
92 | /* when the default delay is a value other than zero: */
93 | --transition-delay: [default-delay];
94 | }
95 |
96 | /* configurable with the "transitionProperty" theme object */
97 | .transition-none {
98 | transition-property: none;
99 | transition-duration: 250ms;
100 | transition-duration: var(--transition-duration);
101 | }
102 | .transition-all {
103 | transition-property: all;
104 | transition-duration: 250ms;
105 | transition-duration: var(--transition-duration);
106 | }
107 | .transition {
108 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
109 | transition-duration: 250ms;
110 | transition-duration: var(--transition-duration);
111 | }
112 | .transition-[property-key] {
113 | transition-property: [property-value];
114 | /* when the default duration is a value other than zero: */
115 | transition-duration: [default-duration];
116 | transition-duration: var(--transition-duration);
117 | /* when the default timing function is a value other than "ease": */
118 | transition-timing-function: [default-timing-function];
119 | transition-timing-function: var(--transition-timing-function);
120 | /* when the default delay is a value other than zero: */
121 | transition-delay: [default-delay];
122 | transition-delay: var(--transition-delay);
123 | }
124 |
125 | /* configurable with the "transitionDuration" theme object */
126 | .transition-0 {
127 | --transition-duration: 0ms;
128 | transition-duration: 0ms;
129 | transition-duration: var(--transition-duration);
130 | }
131 | .transition-50 {
132 | --transition-duration: 50ms;
133 | transition-duration: 50ms;
134 | transition-duration: var(--transition-duration);
135 | }
136 | .transition-[duration-key] {
137 | transition-duration: [duration-value];
138 | /* when the default duration is a value other than zero: */
139 | --transition-duration: [duration-value];
140 | transition-duration: var(--transition-duration);
141 | /* when the default timing function is a value other than "ease": */
142 | transition-timing-function: [default-timing-function];
143 | transition-timing-function: var(--transition-timing-function);
144 | /* when the default delay is a value other than zero: */
145 | transition-delay: [default-delay];
146 | transition-delay: var(--transition-delay);
147 | }
148 |
149 | /* configurable with the "transitionTimingFunction" theme object */
150 | .transition-linear {
151 | transition-timing-function: linear;
152 | }
153 | .transition-ease {
154 | transition-timing-function: ease;
155 | }
156 | .transition-[timing-function-key] {
157 | transition-timing-function: [timing-function-value];
158 | /* when the default timing function is a value other than "ease": */
159 | --transition-timing-function: [timing-function-value];
160 | transition-timing-function: var(--transition-timing-function);
161 | }
162 |
163 | /* configurable with the "transitionDelay" theme object */
164 | .transition-delay-0 {
165 | transition-delay: 0ms;
166 | }
167 | .transition-delay-100 {
168 | transition-delay: 100ms;
169 | }
170 | .transition-delay-[key] {
171 | transition-delay: [value];
172 | /* when the default delay is a value other than zero: */
173 | --transition-delay: [value];
174 | transition-delay: var(--transition-delay);
175 | }
176 |
177 | /* configurable with the "willChange" theme object */
178 | .will-change {
179 | will-change: contents;
180 | }
181 | .will-change-auto {
182 | will-change: auto;
183 | }
184 | .will-change-[key] {
185 | will-change: [value];
186 | }
187 | ```
188 |
189 | Which you can then use in your HTML like this:
190 |
191 | ```html
192 |
195 |
196 |
199 | ```
200 |
201 | Note: The `transitionProperty`, `transitionDuration`, `transitionTimingFunction`, and `transitionDelay` theme objects accept a `default` key. For `transitionProperty`, it generates a simple `transition` class (instead of `transition-default`), but for the other three, `default` doesn’t generate any class; it is used to define a custom property on all elements and pseudo-elements (`*, *::before, *::after`) if the value differs from the CSS-defined default. These custom properties are then used to set actual properties on elements that have a `transition-[property]` or `transition-[duration]` class, so that you don’t have to define a duration, timing function, or delay every time.
202 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | const time = time => _.isNumber(time) ? `${time}ms` : time;
4 |
5 | module.exports = function() {
6 | return ({ theme, variants, e, addBase, addUtilities }) => {
7 | const defaultPropertyTheme = {
8 | 'none': 'none',
9 | 'all': 'all',
10 | 'default': ['background-color', 'border-color', 'color', 'fill', 'stroke', 'opacity', 'box-shadow', 'transform'],
11 | 'colors': ['background-color', 'border-color', 'color', 'fill', 'stroke'],
12 | 'bg': 'background-color',
13 | 'border': 'border-color',
14 | 'color': 'color',
15 | 'opacity': 'opacity',
16 | 'shadow': 'box-shadow',
17 | 'transform': 'transform',
18 | };
19 | const defaultPropertyVariants = ['responsive'];
20 | const defaultDurationTheme = {
21 | 'default': '250ms',
22 | '0': '0ms',
23 | '50': '50ms',
24 | '75': '75ms',
25 | '100': '100ms',
26 | '150': '150ms',
27 | '200': '200ms',
28 | '250': '250ms',
29 | '300': '300ms',
30 | '400': '400ms',
31 | '500': '500ms',
32 | '750': '750ms',
33 | '1000': '1000ms',
34 | };
35 | const defaultDurationVariants = ['responsive'];
36 | const defaultTimingFunctionTheme = {
37 | 'default': 'ease',
38 | 'linear': 'linear',
39 | 'ease': 'ease',
40 | 'ease-in': 'ease-in',
41 | 'ease-out': 'ease-out',
42 | 'ease-in-out': 'ease-in-out',
43 | };
44 | const defaultTimingFunctionVariants = ['responsive'];
45 | const defaultDelayTheme = {
46 | 'default': '0ms',
47 | '0': '0ms',
48 | '100': '100ms',
49 | '250': '250ms',
50 | '500': '500ms',
51 | '750': '750ms',
52 | '1000': '1000ms',
53 | };
54 | const defaultDelayVariants = ['responsive'];
55 | const defaultWillChangeTheme = {
56 | 'auto': 'auto',
57 | 'scroll': 'scroll-position',
58 | 'contents': 'contents',
59 | 'opacity': 'opacity',
60 | 'transform': 'transform',
61 | };
62 | const defaultWillChangeVariants = ['responsive'];
63 |
64 | const propertyTheme = theme('transitionProperty', defaultPropertyTheme);
65 | const propertyVariants = variants('transitionProperty', defaultPropertyVariants);
66 | const durationTheme = theme('transitionDuration', defaultDurationTheme);
67 | const durationVariants = variants('transitionDuration', defaultDurationVariants);
68 | const timingFunctionTheme = theme('transitionTimingFunction', defaultTimingFunctionTheme);
69 | const timingFunctionVariants = variants('transitionTimingFunction', defaultTimingFunctionVariants);
70 | const delayTheme = theme('transitionDelay', defaultDelayTheme);
71 | const delayVariants = variants('transitionDelay', defaultDelayVariants);
72 | const willChangeTheme = theme('willChange', defaultWillChangeTheme);
73 | const willChangeVariants = variants('willChange', defaultWillChangeVariants);
74 |
75 | const defaultDuration = time(_.defaults({}, durationTheme, defaultDurationTheme).default);
76 | const defaultTimingFunction = _.defaults({}, timingFunctionTheme, defaultTimingFunctionTheme).default;
77 | const defaultDelay = time(_.defaults({}, delayTheme, defaultDelayTheme).default);
78 |
79 | const baseDuration = _.includes(['0', '0s', '0ms'], defaultDuration) ? null : defaultDuration;
80 | const baseTimingFunction = defaultTimingFunction === 'ease' ? null : defaultTimingFunction;
81 | const baseDelay = _.includes(['0', '0s', '0ms'], defaultDelay) ? null : defaultDelay;
82 |
83 | const baseStyles = {
84 | ...(function() {
85 | if (baseDuration === null && baseTimingFunction === null && baseDelay === null) {
86 | return {};
87 | }
88 | return {
89 | '*, *::before, *::after': {
90 | '--transition-duration': baseDuration,
91 | '--transition-timing-function': baseTimingFunction,
92 | '--transition-delay': baseDelay,
93 | }
94 | };
95 | })(),
96 | };
97 |
98 | const durationStyles = value => {
99 | if (baseDuration === null) {
100 | return {
101 | transitionDuration: time(value),
102 | };
103 | }
104 | return {
105 | '--transition-duration': time(value),
106 | transitionDuration: [time(value), 'var(--transition-duration)'],
107 | };
108 | };
109 |
110 | const timingFunctionStyles = value => {
111 | if (baseTimingFunction === null) {
112 | return {
113 | transitionTimingFunction: value,
114 | };
115 | }
116 | return {
117 | '--transition-timing-function': value,
118 | transitionTimingFunction: [value, 'var(--transition-timing-function)'],
119 | };
120 | };
121 |
122 | const delayStyles = value => {
123 | if (baseDelay === null) {
124 | return {
125 | transitionDelay: time(value),
126 | };
127 | }
128 | return {
129 | '--transition-delay': time(value),
130 | transitionDelay: [time(value), 'var(--transition-delay)'],
131 | };
132 | };
133 |
134 | const defaultDurationStyles = {
135 | transitionDuration: baseDuration === null ? null : [baseDuration, 'var(--transition-duration)'],
136 | };
137 |
138 | const defaultTimingFunctionStyles = {
139 | transitionTimingFunction: baseTimingFunction === null ? null : [baseTimingFunction, 'var(--transition-timing-function)'],
140 | };
141 |
142 | const defaultDelayStyles = {
143 | transitionDelay: baseDelay === null ? null : [baseDelay, 'var(--transition-delay)'],
144 | };
145 |
146 | const propertyUtilities = _.fromPairs(
147 | _.map(propertyTheme, (value, modifier) => {
148 | return [
149 | `.${e(`transition${modifier === 'default' ? '' : `-${modifier}`}`)}`,
150 | {
151 | transitionProperty: _.isArray(value) ? value.join(', ') : value,
152 | ...defaultDurationStyles,
153 | ...defaultTimingFunctionStyles,
154 | ...defaultDelayStyles,
155 | },
156 | ];
157 | })
158 | );
159 |
160 | const durationUtilities = _.fromPairs(
161 | _.map(durationTheme, (value, modifier) => {
162 | if (modifier === 'default') {
163 | return [];
164 | }
165 | return [
166 | `.${e(`transition-${modifier}`)}`,
167 | {
168 | ...durationStyles(value),
169 | ...defaultTimingFunctionStyles,
170 | ...defaultDelayStyles,
171 | },
172 | ];
173 | })
174 | );
175 |
176 | const timingFunctionUtilities = _.fromPairs(
177 | _.map(timingFunctionTheme, (value, modifier) => {
178 | if (modifier === 'default') {
179 | return [];
180 | }
181 | return [
182 | `.${e(`transition-${modifier}`)}`,
183 | {
184 | ...timingFunctionStyles(value),
185 | },
186 | ];
187 | })
188 | );
189 |
190 | const delayUtilities = _.fromPairs(
191 | _.map(delayTheme, (value, modifier) => {
192 | if (modifier === 'default') {
193 | return [];
194 | }
195 | return [
196 | `.${e(`transition-delay-${modifier}`)}`,
197 | {
198 | ...delayStyles(value),
199 | },
200 | ];
201 | })
202 | );
203 |
204 | const willChangeUtilities = _.fromPairs(
205 | _.map(willChangeTheme, (value, modifier) => {
206 | return [
207 | `.${e(`will-change-${modifier}`)}`,
208 | {
209 | willChange: value,
210 | },
211 | ];
212 | })
213 | );
214 |
215 | addBase(baseStyles);
216 | addUtilities(propertyUtilities, propertyVariants);
217 | addUtilities(durationUtilities, durationVariants);
218 | addUtilities(timingFunctionUtilities, timingFunctionVariants);
219 | addUtilities(delayUtilities, delayVariants);
220 | addUtilities(willChangeUtilities, willChangeVariants);
221 | };
222 | };
223 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tailwindcss-transitions",
3 | "version": "2.2.0",
4 | "description": "Tailwind CSS plugin to generate transition utilities",
5 | "author": "Benoît Rouleau ",
6 | "license": "ISC",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/benface/tailwindcss-transitions.git"
10 | },
11 | "bugs": "https://github.com/benface/tailwindcss-transitions/issues",
12 | "homepage": "https://github.com/benface/tailwindcss-transitions",
13 | "scripts": {
14 | "test": "jest",
15 | "release": "f(){ release-it $1 && github-release-from-changelog ;};f"
16 | },
17 | "dependencies": {
18 | "lodash": "^4.17.15"
19 | },
20 | "devDependencies": {
21 | "github-release-from-changelog": "^2.1.0",
22 | "jest": "^25.1.0",
23 | "jest-matcher-css": "^1.1.0",
24 | "postcss": "^7.0.26",
25 | "release-it": "^12.4.3",
26 | "tailwindcss": "^1.1.4"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 | const cssMatcher = require('jest-matcher-css');
3 | const postcss = require('postcss');
4 | const tailwindcss = require('tailwindcss');
5 | const transitionsPlugin = require('./index.js');
6 |
7 | const generatePluginCss = (config) => {
8 | return postcss(
9 | tailwindcss(
10 | _.merge({
11 | theme: {
12 | screens: {
13 | 'sm': '640px',
14 | },
15 | },
16 | corePlugins: false,
17 | plugins: [
18 | transitionsPlugin(),
19 | ],
20 | }, config)
21 | )
22 | )
23 | .process('@tailwind base; @tailwind utilities', {
24 | from: undefined,
25 | })
26 | .then(result => {
27 | return result.css;
28 | });
29 | };
30 |
31 | expect.extend({
32 | toMatchCss: cssMatcher,
33 | });
34 |
35 | test('the plugin generates some utilities and responsive variants by default', () => {
36 | return generatePluginCss().then(css => {
37 | expect(css).toMatchCss(`
38 | *, *::before, *::after {
39 | --transition-duration: 250ms;
40 | }
41 | .transition-none {
42 | transition-property: none;
43 | transition-duration: 250ms;
44 | transition-duration: var(--transition-duration);
45 | }
46 | .transition-all {
47 | transition-property: all;
48 | transition-duration: 250ms;
49 | transition-duration: var(--transition-duration);
50 | }
51 | .transition {
52 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
53 | transition-duration: 250ms;
54 | transition-duration: var(--transition-duration);
55 | }
56 | .transition-colors {
57 | transition-property: background-color, border-color, color, fill, stroke;
58 | transition-duration: 250ms;
59 | transition-duration: var(--transition-duration);
60 | }
61 | .transition-bg {
62 | transition-property: background-color;
63 | transition-duration: 250ms;
64 | transition-duration: var(--transition-duration);
65 | }
66 | .transition-border {
67 | transition-property: border-color;
68 | transition-duration: 250ms;
69 | transition-duration: var(--transition-duration);
70 | }
71 | .transition-color {
72 | transition-property: color;
73 | transition-duration: 250ms;
74 | transition-duration: var(--transition-duration);
75 | }
76 | .transition-opacity {
77 | transition-property: opacity;
78 | transition-duration: 250ms;
79 | transition-duration: var(--transition-duration);
80 | }
81 | .transition-shadow {
82 | transition-property: box-shadow;
83 | transition-duration: 250ms;
84 | transition-duration: var(--transition-duration);
85 | }
86 | .transition-transform {
87 | transition-property: transform;
88 | transition-duration: 250ms;
89 | transition-duration: var(--transition-duration);
90 | }
91 | .transition-0 {
92 | --transition-duration: 0ms;
93 | transition-duration: 0ms;
94 | transition-duration: var(--transition-duration);
95 | }
96 | .transition-50 {
97 | --transition-duration: 50ms;
98 | transition-duration: 50ms;
99 | transition-duration: var(--transition-duration);
100 | }
101 | .transition-75 {
102 | --transition-duration: 75ms;
103 | transition-duration: 75ms;
104 | transition-duration: var(--transition-duration);
105 | }
106 | .transition-100 {
107 | --transition-duration: 100ms;
108 | transition-duration: 100ms;
109 | transition-duration: var(--transition-duration);
110 | }
111 | .transition-150 {
112 | --transition-duration: 150ms;
113 | transition-duration: 150ms;
114 | transition-duration: var(--transition-duration);
115 | }
116 | .transition-200 {
117 | --transition-duration: 200ms;
118 | transition-duration: 200ms;
119 | transition-duration: var(--transition-duration);
120 | }
121 | .transition-250 {
122 | --transition-duration: 250ms;
123 | transition-duration: 250ms;
124 | transition-duration: var(--transition-duration);
125 | }
126 | .transition-300 {
127 | --transition-duration: 300ms;
128 | transition-duration: 300ms;
129 | transition-duration: var(--transition-duration);
130 | }
131 | .transition-400 {
132 | --transition-duration: 400ms;
133 | transition-duration: 400ms;
134 | transition-duration: var(--transition-duration);
135 | }
136 | .transition-500 {
137 | --transition-duration: 500ms;
138 | transition-duration: 500ms;
139 | transition-duration: var(--transition-duration);
140 | }
141 | .transition-750 {
142 | --transition-duration: 750ms;
143 | transition-duration: 750ms;
144 | transition-duration: var(--transition-duration);
145 | }
146 | .transition-1000 {
147 | --transition-duration: 1000ms;
148 | transition-duration: 1000ms;
149 | transition-duration: var(--transition-duration);
150 | }
151 | .transition-linear {
152 | transition-timing-function: linear;
153 | }
154 | .transition-ease {
155 | transition-timing-function: ease;
156 | }
157 | .transition-ease-in {
158 | transition-timing-function: ease-in;
159 | }
160 | .transition-ease-out {
161 | transition-timing-function: ease-out;
162 | }
163 | .transition-ease-in-out {
164 | transition-timing-function: ease-in-out;
165 | }
166 | .transition-delay-0 {
167 | transition-delay: 0ms;
168 | }
169 | .transition-delay-100 {
170 | transition-delay: 100ms;
171 | }
172 | .transition-delay-250 {
173 | transition-delay: 250ms;
174 | }
175 | .transition-delay-500 {
176 | transition-delay: 500ms;
177 | }
178 | .transition-delay-750 {
179 | transition-delay: 750ms;
180 | }
181 | .transition-delay-1000 {
182 | transition-delay: 1000ms;
183 | }
184 | .will-change-auto {
185 | will-change: auto;
186 | }
187 | .will-change-scroll {
188 | will-change: scroll-position;
189 | }
190 | .will-change-contents {
191 | will-change: contents;
192 | }
193 | .will-change-opacity {
194 | will-change: opacity;
195 | }
196 | .will-change-transform {
197 | will-change: transform;
198 | }
199 | @media (min-width: 640px) {
200 | .sm\\:transition-none {
201 | transition-property: none;
202 | transition-duration: 250ms;
203 | transition-duration: var(--transition-duration);
204 | }
205 | .sm\\:transition-all {
206 | transition-property: all;
207 | transition-duration: 250ms;
208 | transition-duration: var(--transition-duration);
209 | }
210 | .sm\\:transition {
211 | transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
212 | transition-duration: 250ms;
213 | transition-duration: var(--transition-duration);
214 | }
215 | .sm\\:transition-colors {
216 | transition-property: background-color, border-color, color, fill, stroke;
217 | transition-duration: 250ms;
218 | transition-duration: var(--transition-duration);
219 | }
220 | .sm\\:transition-bg {
221 | transition-property: background-color;
222 | transition-duration: 250ms;
223 | transition-duration: var(--transition-duration);
224 | }
225 | .sm\\:transition-border {
226 | transition-property: border-color;
227 | transition-duration: 250ms;
228 | transition-duration: var(--transition-duration);
229 | }
230 | .sm\\:transition-color {
231 | transition-property: color;
232 | transition-duration: 250ms;
233 | transition-duration: var(--transition-duration);
234 | }
235 | .sm\\:transition-opacity {
236 | transition-property: opacity;
237 | transition-duration: 250ms;
238 | transition-duration: var(--transition-duration);
239 | }
240 | .sm\\:transition-shadow {
241 | transition-property: box-shadow;
242 | transition-duration: 250ms;
243 | transition-duration: var(--transition-duration);
244 | }
245 | .sm\\:transition-transform {
246 | transition-property: transform;
247 | transition-duration: 250ms;
248 | transition-duration: var(--transition-duration);
249 | }
250 | .sm\\:transition-0 {
251 | --transition-duration: 0ms;
252 | transition-duration: 0ms;
253 | transition-duration: var(--transition-duration);
254 | }
255 | .sm\\:transition-50 {
256 | --transition-duration: 50ms;
257 | transition-duration: 50ms;
258 | transition-duration: var(--transition-duration);
259 | }
260 | .sm\\:transition-75 {
261 | --transition-duration: 75ms;
262 | transition-duration: 75ms;
263 | transition-duration: var(--transition-duration);
264 | }
265 | .sm\\:transition-100 {
266 | --transition-duration: 100ms;
267 | transition-duration: 100ms;
268 | transition-duration: var(--transition-duration);
269 | }
270 | .sm\\:transition-150 {
271 | --transition-duration: 150ms;
272 | transition-duration: 150ms;
273 | transition-duration: var(--transition-duration);
274 | }
275 | .sm\\:transition-200 {
276 | --transition-duration: 200ms;
277 | transition-duration: 200ms;
278 | transition-duration: var(--transition-duration);
279 | }
280 | .sm\\:transition-250 {
281 | --transition-duration: 250ms;
282 | transition-duration: 250ms;
283 | transition-duration: var(--transition-duration);
284 | }
285 | .sm\\:transition-300 {
286 | --transition-duration: 300ms;
287 | transition-duration: 300ms;
288 | transition-duration: var(--transition-duration);
289 | }
290 | .sm\\:transition-400 {
291 | --transition-duration: 400ms;
292 | transition-duration: 400ms;
293 | transition-duration: var(--transition-duration);
294 | }
295 | .sm\\:transition-500 {
296 | --transition-duration: 500ms;
297 | transition-duration: 500ms;
298 | transition-duration: var(--transition-duration);
299 | }
300 | .sm\\:transition-750 {
301 | --transition-duration: 750ms;
302 | transition-duration: 750ms;
303 | transition-duration: var(--transition-duration);
304 | }
305 | .sm\\:transition-1000 {
306 | --transition-duration: 1000ms;
307 | transition-duration: 1000ms;
308 | transition-duration: var(--transition-duration);
309 | }
310 | .sm\\:transition-linear {
311 | transition-timing-function: linear;
312 | }
313 | .sm\\:transition-ease {
314 | transition-timing-function: ease;
315 | }
316 | .sm\\:transition-ease-in {
317 | transition-timing-function: ease-in;
318 | }
319 | .sm\\:transition-ease-out {
320 | transition-timing-function: ease-out;
321 | }
322 | .sm\\:transition-ease-in-out {
323 | transition-timing-function: ease-in-out;
324 | }
325 | .sm\\:transition-delay-0 {
326 | transition-delay: 0ms;
327 | }
328 | .sm\\:transition-delay-100 {
329 | transition-delay: 100ms;
330 | }
331 | .sm\\:transition-delay-250 {
332 | transition-delay: 250ms;
333 | }
334 | .sm\\:transition-delay-500 {
335 | transition-delay: 500ms;
336 | }
337 | .sm\\:transition-delay-750 {
338 | transition-delay: 750ms;
339 | }
340 | .sm\\:transition-delay-1000 {
341 | transition-delay: 1000ms;
342 | }
343 | .sm\\:will-change-auto {
344 | will-change: auto;
345 | }
346 | .sm\\:will-change-scroll {
347 | will-change: scroll-position;
348 | }
349 | .sm\\:will-change-contents {
350 | will-change: contents;
351 | }
352 | .sm\\:will-change-opacity {
353 | will-change: opacity;
354 | }
355 | .sm\\:will-change-transform {
356 | will-change: transform;
357 | }
358 | }
359 | `);
360 | });
361 | });
362 |
363 | test('the default duration, timing function and delay can be changed', () => {
364 | return generatePluginCss({
365 | theme: {
366 | transitionProperty: {
367 | 'none': 'none',
368 | 'all': 'all',
369 | },
370 | transitionDuration: {
371 | 'default': '500ms',
372 | 'fast': '250ms',
373 | },
374 | transitionTimingFunction: {
375 | 'default': 'linear',
376 | 'ease': 'ease',
377 | },
378 | transitionDelay: {
379 | 'default': '100ms',
380 | 'none': '0ms',
381 | },
382 | willChange: {},
383 | },
384 | variants: {
385 | transitionProperty: [],
386 | transitionDuration: [],
387 | transitionTimingFunction: [],
388 | transitionDelay: [],
389 | willChange: [],
390 | },
391 | }).then(css => {
392 | expect(css).toMatchCss(`
393 | *, *::before, *::after {
394 | --transition-duration: 500ms;
395 | --transition-timing-function: linear;
396 | --transition-delay: 100ms;
397 | }
398 | .transition-none {
399 | transition-property: none;
400 | transition-duration: 500ms;
401 | transition-duration: var(--transition-duration);
402 | transition-timing-function: linear;
403 | transition-timing-function: var(--transition-timing-function);
404 | transition-delay: 100ms;
405 | transition-delay: var(--transition-delay);
406 | }
407 | .transition-all {
408 | transition-property: all;
409 | transition-duration: 500ms;
410 | transition-duration: var(--transition-duration);
411 | transition-timing-function: linear;
412 | transition-timing-function: var(--transition-timing-function);
413 | transition-delay: 100ms;
414 | transition-delay: var(--transition-delay);
415 | }
416 | .transition-fast {
417 | --transition-duration: 250ms;
418 | transition-duration: 250ms;
419 | transition-duration: var(--transition-duration);
420 | transition-timing-function: linear;
421 | transition-timing-function: var(--transition-timing-function);
422 | transition-delay: 100ms;
423 | transition-delay: var(--transition-delay);
424 | }
425 | .transition-ease {
426 | --transition-timing-function: ease;
427 | transition-timing-function: ease;
428 | transition-timing-function: var(--transition-timing-function);
429 | }
430 | .transition-delay-none {
431 | --transition-delay: 0ms;
432 | transition-delay: 0ms;
433 | transition-delay: var(--transition-delay);
434 | }
435 | `);
436 | });
437 | });
438 |
439 | test('the default duration can be removed', () => {
440 | return generatePluginCss({
441 | theme: {
442 | transitionProperty: {
443 | 'none': 'none',
444 | 'all': 'all',
445 | },
446 | transitionDuration: {
447 | 'default': '0ms',
448 | 'fast': '250ms',
449 | 'medium': '500ms',
450 | 'slow': '1000ms',
451 | },
452 | transitionTimingFunction: {
453 | 'ease': 'ease',
454 | },
455 | transitionDelay: {
456 | '0': '0ms',
457 | },
458 | willChange: {},
459 | },
460 | variants: {
461 | transitionProperty: [],
462 | transitionDuration: [],
463 | transitionTimingFunction: [],
464 | transitionDelay: [],
465 | willChange: [],
466 | },
467 | }).then(css => {
468 | expect(css).toMatchCss(`
469 | .transition-none {
470 | transition-property: none;
471 | }
472 | .transition-all {
473 | transition-property: all;
474 | }
475 | .transition-fast {
476 | transition-duration: 250ms;
477 | }
478 | .transition-medium {
479 | transition-duration: 500ms;
480 | }
481 | .transition-slow {
482 | transition-duration: 1000ms;
483 | }
484 | .transition-ease {
485 | transition-timing-function: ease;
486 | }
487 | .transition-delay-0 {
488 | transition-delay: 0ms;
489 | }
490 | `);
491 | });
492 | });
493 |
494 | test('variants can be customized', () => {
495 | return generatePluginCss({
496 | theme: {
497 | transitionProperty: {
498 | 'none': 'none',
499 | 'all': 'all',
500 | 'bg': 'background-color',
501 | },
502 | transitionDuration: {
503 | '500': '500ms',
504 | },
505 | transitionTimingFunction: {
506 | 'default': 'linear',
507 | 'ease': 'ease',
508 | 'linear': 'linear',
509 | },
510 | transitionDelay: {
511 | '500': '500ms',
512 | },
513 | willChange: {
514 | 'contents': 'contents',
515 | },
516 | },
517 | variants: {
518 | transitionProperty: ['hover'],
519 | transitionDuration: ['active'],
520 | transitionTimingFunction: ['group-hover', 'focus'],
521 | transitionDelay: ['responsive'],
522 | willChange: [],
523 | },
524 | }).then(css => {
525 | expect(css).toMatchCss(`
526 | *, *::before, *::after {
527 | --transition-duration: 250ms;
528 | --transition-timing-function: linear;
529 | }
530 | .transition-none {
531 | transition-property: none;
532 | transition-duration: 250ms;
533 | transition-duration: var(--transition-duration);
534 | transition-timing-function: linear;
535 | transition-timing-function: var(--transition-timing-function);
536 | }
537 | .transition-all {
538 | transition-property: all;
539 | transition-duration: 250ms;
540 | transition-duration: var(--transition-duration);
541 | transition-timing-function: linear;
542 | transition-timing-function: var(--transition-timing-function);
543 | }
544 | .transition-bg {
545 | transition-property: background-color;
546 | transition-duration: 250ms;
547 | transition-duration: var(--transition-duration);
548 | transition-timing-function: linear;
549 | transition-timing-function: var(--transition-timing-function);
550 | }
551 | .hover\\:transition-none:hover {
552 | transition-property: none;
553 | transition-duration: 250ms;
554 | transition-duration: var(--transition-duration);
555 | transition-timing-function: linear;
556 | transition-timing-function: var(--transition-timing-function);
557 | }
558 | .hover\\:transition-all:hover {
559 | transition-property: all;
560 | transition-duration: 250ms;
561 | transition-duration: var(--transition-duration);
562 | transition-timing-function: linear;
563 | transition-timing-function: var(--transition-timing-function);
564 | }
565 | .hover\\:transition-bg:hover {
566 | transition-property: background-color;
567 | transition-duration: 250ms;
568 | transition-duration: var(--transition-duration);
569 | transition-timing-function: linear;
570 | transition-timing-function: var(--transition-timing-function);
571 | }
572 | .transition-500 {
573 | --transition-duration: 500ms;
574 | transition-duration: 500ms;
575 | transition-duration: var(--transition-duration);
576 | transition-timing-function: linear;
577 | transition-timing-function: var(--transition-timing-function);
578 | }
579 | .active\\:transition-500:active {
580 | --transition-duration: 500ms;
581 | transition-duration: 500ms;
582 | transition-duration: var(--transition-duration);
583 | transition-timing-function: linear;
584 | transition-timing-function: var(--transition-timing-function);
585 | }
586 | .transition-ease {
587 | --transition-timing-function: ease;
588 | transition-timing-function: ease;
589 | transition-timing-function: var(--transition-timing-function);
590 | }
591 | .transition-linear {
592 | --transition-timing-function: linear;
593 | transition-timing-function: linear;
594 | transition-timing-function: var(--transition-timing-function);
595 | }
596 | .group:hover .group-hover\\:transition-ease {
597 | --transition-timing-function: ease;
598 | transition-timing-function: ease;
599 | transition-timing-function: var(--transition-timing-function);
600 | }
601 | .group:hover .group-hover\\:transition-linear {
602 | --transition-timing-function: linear;
603 | transition-timing-function: linear;
604 | transition-timing-function: var(--transition-timing-function);
605 | }
606 | .focus\\:transition-ease:focus {
607 | --transition-timing-function: ease;
608 | transition-timing-function: ease;
609 | transition-timing-function: var(--transition-timing-function);
610 | }
611 | .focus\\:transition-linear:focus {
612 | --transition-timing-function: linear;
613 | transition-timing-function: linear;
614 | transition-timing-function: var(--transition-timing-function);
615 | }
616 | .transition-delay-500 {
617 | transition-delay: 500ms;
618 | }
619 | .will-change-contents {
620 | will-change: contents;
621 | }
622 | @media (min-width: 640px) {
623 | .sm\\:transition-delay-500 {
624 | transition-delay: 500ms;
625 | }
626 | }
627 | `);
628 | });
629 | });
630 |
631 | test('durations and delays defined as numbers are translated to ms', () => {
632 | return generatePluginCss({
633 | theme: {
634 | transitionProperty: {},
635 | transitionDuration: {
636 | 'default': 500,
637 | '250': 250,
638 | },
639 | transitionTimingFunction: {},
640 | transitionDelay: {
641 | '500': 500,
642 | },
643 | willChange: {},
644 | },
645 | variants: {
646 | transitionProperty: [],
647 | transitionDuration: [],
648 | transitionTimingFunction: [],
649 | transitionDelay: [],
650 | willChange: [],
651 | },
652 | }).then(css => {
653 | expect(css).toMatchCss(`
654 | *, *::before, *::after {
655 | --transition-duration: 500ms;
656 | }
657 | .transition-250 {
658 | --transition-duration: 250ms;
659 | transition-duration: 250ms;
660 | transition-duration: var(--transition-duration);
661 | }
662 | .transition-delay-500 {
663 | transition-delay: 500ms;
664 | }
665 | `);
666 | });
667 | });
668 |
--------------------------------------------------------------------------------