├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── publish.yml
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── eslint.config.mjs
├── jest
├── content
│ ├── composition.html
│ ├── delay.html
│ ├── direction.html
│ ├── duration.html
│ ├── fill-mode.html
│ ├── iteration-count.html
│ ├── play-state.html
│ ├── predefined-animations.html
│ └── timing-function.html
├── customMatchers.js
├── index.test.js
└── legacy
│ ├── index.test.js
│ └── package.json
├── package.json
├── public
└── index.html
├── resources
└── app.css
└── src
├── compat
├── composition.js
├── delay.js
├── direction.js
├── duration.js
├── fill-mode.js
├── helper
│ └── bare-values.js
├── iteration-count.js
├── play-state.js
├── theme.js
└── timing-function.js
├── index.css
├── index.d.ts
├── index.js
└── utilities
├── composition.css
├── delay.css
├── direction.css
├── duration.css
├── fill-mode.css
├── iteration-count.css
├── play-state.css
├── predefined-animations.css
└── timing-function.css
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 | - uses: actions/setup-node@v3
13 | with:
14 | node-version: '20.x'
15 | - run: npm install
16 | - run: npm install --prefix jest/legacy
17 | - run: npm test
18 |
19 | publish:
20 | needs: test
21 | runs-on: ubuntu-latest
22 | steps:
23 | - uses: actions/checkout@v3
24 | - uses: actions/setup-node@v3
25 | with:
26 | node-version: '20.x'
27 | registry-url: 'https://registry.npmjs.org'
28 | - run: npm version --no-git-tag-version ${{ github.event.release.tag_name }}
29 | - run: npm publish
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | test:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | node-version: [20.x, 22.x]
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Use Node.js ${{ matrix.node-version }}
14 | uses: actions/setup-node@v3
15 | with:
16 | node-version: ${{ matrix.node-version }}
17 | - run: npm install
18 | - run: npm install --prefix jest/legacy
19 | - run: npm run lint
20 | - run: npm run test
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 | package-lock.json
4 | yarn.lock
5 | .npm
6 |
7 | # logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # misc
15 | /public/tailwind.css
16 | /public/app.css
17 | /.build
18 | /.idea
19 | /.nvmrc
20 | /.vscode
21 | *.local
22 | .DS_Store
23 | .env
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 new-data-services GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tailwind CSS Animated
2 |
3 | Extended animation utilities for Tailwind CSS
4 | https://tailwindcss-animated.com
5 |
6 | ## Installation
7 |
8 | First, install the plugin via npm:
9 |
10 | ```sh
11 | npm install tailwindcss-animated
12 | ```
13 |
14 | ## Import
15 |
16 | Second, import it alongside Tailwind CSS in your CSS file:
17 |
18 | ```css
19 | /* tailwind css v4.x */
20 | @import "tailwindcss";
21 | @import "tailwindcss-animated";
22 | ```
23 |
24 | Or, if you are using **Tailwind CSS v3.x** or the legacy JavaScript configuration file, import the plugin like this:
25 |
26 | ```js
27 | // tailwind.config.js
28 | module.exports = {
29 | // ...
30 | plugins: [
31 | require('tailwindcss-animated')
32 | ],
33 | }
34 | ```
35 |
36 | ## Usage
37 |
38 | This plugin brings various utility classes as well as several ready-to-use CSS animations. Here are some simple examples:
39 |
40 | ```html
41 |
42 | Hej, look at me!
43 |
44 |
45 |
46 | Wait a bit, then jump right in.
47 |
48 | ```
49 |
50 | ### Ready-to-use animations
51 |
52 | There are several animations that can be integrated with a single utility class. These extend the Spin, Ping and Pulse animations of Tailwind CSS.
53 |
54 | Open the configurator to see them in action:
55 | https://tailwindcss-animated.com/configurator.html
56 |
57 | All animations can be customized with the utility classes below.
58 |
59 | ### Duration
60 |
61 | | Class | Properties |
62 | |-----|-----|
63 | | animate-duration-75 | animation-duration: 75ms; |
64 | | animate-duration-100 | animation-duration: 100ms; |
65 | | animate-duration-150 | animation-duration: 150ms; |
66 | | animate-duration-200 | animation-duration: 200ms; |
67 | | animate-duration-300 | animation-duration: 300ms; |
68 | | animate-duration-500 | animation-duration: 500ms; |
69 | | animate-duration-700 | animation-duration: 700ms; |
70 | | animate-duration-1000 | animation-duration: 1000ms; |
71 | | animate-duration-[*\*] | animation-duration: *\* ms; |
72 | | animate-duration-*\* [*](#custom-properties-and-bare-values) | animation-duration: *\* ms; |
73 | | animate-duration-(*\*) [*](#custom-properties-and-bare-values) | animation-duration: var(*\*); |
74 |
75 | ### Delay
76 |
77 | | Class | Properties |
78 | |-----|-----|
79 | | animate-delay-none | animation-delay: 0ms; |
80 | | animate-delay-75 | animation-delay: 75ms; |
81 | | animate-delay-100 | animation-delay: 100ms; |
82 | | animate-delay-150 | animation-delay: 150ms; |
83 | | animate-delay-200 | animation-delay: 200ms; |
84 | | animate-delay-300 | animation-delay: 300ms; |
85 | | animate-delay-500 | animation-delay: 500ms; |
86 | | animate-delay-700 | animation-delay: 700ms; |
87 | | animate-delay-1000 | animation-delay: 1000ms; |
88 | | animate-delay-[*\*] | animation-delay: *\* ms; |
89 | | animate-delay-*\* [*](#custom-properties-and-bare-values) | animation-delay: *\* ms; |
90 | | animate-delay-(*\*) [*](#custom-properties-and-bare-values) | animation-delay: var(*\*); |
91 |
92 | ### Direction
93 |
94 | | Class | Properties |
95 | |-----|-----|
96 | | animate-normal | animation-direction: normal; |
97 | | animate-reverse | animation-direction: reverse; |
98 | | animate-alternate | animation-direction: alternate; |
99 | | animate-alternate-reverse | animation-direction: alternate-reverse; |
100 |
101 | ### Iteration Count
102 |
103 | | Class | Properties |
104 | |-----|-----|
105 | | animate-infinite | animation-iteration-count: infinite; |
106 | | animate-once | animation-iteration-count: 1; |
107 | | animate-twice | animation-iteration-count: 2; |
108 | | animate-thrice | animation-iteration-count: 3; |
109 | | animate-iteration-[*\*] | animation-iteration-count: *\*; |
110 | | animate-iteration-*\* [*](#custom-properties-and-bare-values) | animation-iteration-count: *\*; |
111 | | animate-iteration-(*\*) [*](#custom-properties-and-bare-values) | animation-iteration-count: var(*\*); |
112 |
113 | ### Timing Function
114 |
115 | | Class | Properties |
116 | |-----|-----|
117 | | animate-ease | animation-timing-function: ease; |
118 | | animate-ease-linear | animation-timing-function: linear; |
119 | | animate-ease-in | animation-timing-function: cubic-bezier(0.4, 0, 1, 1); |
120 | | animate-ease-out | animation-timing-function: cubic-bezier(0, 0, 0.2, 1); |
121 | | animate-ease-in-out | animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); |
122 | | animate-ease-[*\*] | animation-timing-function: *\*; |
123 | | animate-ease-(*\*) [*](#custom-properties-and-bare-values) | animation-timing-function: var(*\*); |
124 |
125 | ### Fill Mode
126 |
127 | | Class | Properties |
128 | |-----|-----|
129 | | animate-fill-none | animation-fill-mode: normal; |
130 | | animate-fill-forwards | animation-fill-mode: forwards; |
131 | | animate-fill-backwards | animation-fill-mode: backwards; |
132 | | animate-fill-both | animation-fill-mode: both; |
133 |
134 | ### Play State
135 |
136 | | Class | Properties |
137 | |-----|-----|
138 | | animate-run | animation-play-state: running; |
139 | | animate-play | animation-play-state: running; |
140 | | animate-stop | animation-play-state: paused; |
141 | | animate-pause | animation-play-state: paused; |
142 |
143 | ### Composition
144 |
145 | | Class | Properties |
146 | |-----|-----|
147 | | animate-replace | animation-composition: replace; |
148 | | animate-add | animation-composition: add; |
149 | | animate-accumulate | animation-composition: accumulate; |
150 |
151 | ## Variant modifiers and breakpoints
152 |
153 | All variants and breakpoints (hover, focus, lg, ...) work with animations und animation utility classes.
154 |
155 | ```html
156 |
157 |
158 |
159 | ```
160 |
161 | ## Arbitrary values
162 |
163 | Of course, you can use arbitrary values for animations ultilities:
164 |
165 | ```html
166 |
167 |
168 |
169 | ```
170 |
171 | ## Custom properties and bare values
172 |
173 | With **Tailwind CSS v4.0** or newer you can use the shorthand syntax for custom properties. Bare values for delay, duration and iteration utilities also work.
174 |
175 | ```html
176 |
177 |
178 |
179 | ```
180 |
181 | More information on the new arbitrary value syntax can be found in the [Tailwind CSS Docs](https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values).
182 |
183 | ## Override default values
184 |
185 | All animations come with default values for duration, delay and timing function. If you want to overwrite these values globally, you can set the following CSS properties:
186 |
187 | ```css
188 | :root {
189 | --default-animation-duration: 500ms;
190 | --default-animation-delay: 0s;
191 | --default-animation-timing-function: ease;
192 | }
193 | ```
194 |
195 | ## FAQ
196 |
197 | ### How to animate on scroll?
198 |
199 | To run animations when an element enters the viewport, you need JavaScript. (At least until [animation-timeline](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline) has good browser support)
200 |
201 | A good starting point for a JavaScript solution would be the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). Or tools that build on it, such as the [Alpine.js Intersect plugin](https://alpinejs.dev/plugins/intersect) and the [Tailwind CSS Intersection plugin](https://github.com/heidkaemper/tailwindcss-intersect), to name just two.
202 |
203 | ### How to combine multiple animations?
204 |
205 | The simplest approach is to nest two elements:
206 |
207 | ```html
208 |
211 | ```
212 |
213 | ### Can keyframes and offset values be changed?
214 |
215 | Offset positions of predefined animations can't be changed on the fly. But the behavior can still be modified with [animation-composition](#composition) utilities.
216 |
217 | If you need more details on how compositions work, check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-composition).
218 |
219 | ### Does this work with the Play CDN?
220 |
221 | Unfortunately not. The Tailwind CSS Play CDN currently does not support third-party plugins.
222 |
223 | ---
224 |
225 |
226 |
227 |
228 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import eslintPluginJest from 'eslint-plugin-jest'
4 |
5 | export default [
6 | js.configs.recommended,
7 | eslintPluginJest.configs['flat/recommended'],
8 | {
9 | languageOptions: {
10 | ecmaVersion: 2022,
11 | sourceType: 'commonjs',
12 | globals: {
13 | ...globals.node,
14 | },
15 | },
16 | rules: {
17 | 'comma-spacing': ['error', { 'before': false, 'after': true }],
18 | 'comma-style': ['error', 'last'],
19 | 'eol-last': ['error', 'always'],
20 | 'func-call-spacing': ['error', 'never'],
21 | 'key-spacing': ['error', { 'mode': 'minimum', 'beforeColon': false, 'afterColon': true }],
22 | 'keyword-spacing': ['error', { 'before': true, 'after': true }],
23 | 'linebreak-style': ['error', 'unix'],
24 | 'lines-between-class-members': ['error', 'always'],
25 | 'no-console': ['warn', { 'allow': ['warn', 'error'] }],
26 | 'no-empty-function': 'error',
27 | 'no-multiple-empty-lines': ['error', { 'max': 2 }],
28 | 'no-var': 'error',
29 | 'quotes': ['error', 'single'],
30 | 'semi': ['error', 'never'],
31 | 'space-before-blocks': ['error', 'always'],
32 | 'space-before-function-paren': ['error', { 'named': 'never', 'anonymous': 'always', 'asyncArrow': 'always' }],
33 | },
34 | },
35 | ]
36 |
--------------------------------------------------------------------------------
/jest/content/composition.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jest/content/delay.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jest/content/direction.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/jest/content/duration.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jest/content/fill-mode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/jest/content/iteration-count.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/jest/content/play-state.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/jest/content/predefined-animations.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/jest/content/timing-function.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/jest/customMatchers.js:
--------------------------------------------------------------------------------
1 | expect.extend({
2 | toIncludeAll(received, expected) {
3 | function stripped(str) {
4 | return str.replace(/\s/g, '').replace(/;/g, '')
5 | }
6 |
7 | const receivedStripped = stripped(received)
8 |
9 | const pass = Array.isArray(expected) && expected.every(value => receivedStripped.includes(stripped(value)))
10 |
11 | return {
12 | pass,
13 | message: () => pass
14 | ? this.utils.matcherHint('.not.toIncludeAll') +
15 | '\n\n' +
16 | `Expected not to have all of: ${this.utils.printExpected(received)}\n` +
17 | `Received: ${this.utils.printReceived(expected)}`
18 | : this.utils.matcherHint('.toIncludeAll') +
19 | '\n\n' +
20 | `Expected to have all of: ${this.utils.printExpected(expected)}\n` +
21 | `Received: ${this.utils.printReceived(received)}`,
22 | }
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/jest/index.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const postcss = require('postcss')
3 | const tailwindcss = require('@tailwindcss/postcss')
4 |
5 | async function run(file, options = {}) {
6 | const { currentTestName } = expect.getState()
7 |
8 | const config = [
9 | options.scope
10 | ? `@import "tailwindcss/${options.scope}" source(none);`
11 | : '@import "tailwindcss" source(none);',
12 | options?.importType === 'legacy'
13 | ? '@plugin "./../src/";'
14 | : '@import "./../src/";',
15 | `@source "./${file}";`,
16 | ].join('\n')
17 |
18 | const result = await postcss(tailwindcss()).process(config, {
19 | from: `${path.resolve(__filename)}?test=${currentTestName}`,
20 | })
21 |
22 | return result.css
23 | }
24 |
25 | describe('modern tailwind', () => {
26 | it('generates `composition` utilities', async () => {
27 | expect(await run('content/composition.html', { scope: 'utilities' })).toIncludeAll([
28 | '.animate-add { animation-composition: add; }',
29 | '.animate-replace { animation-composition: replace; }',
30 | '.animate-accumulate { animation-composition: accumulate; }',
31 | ])
32 | })
33 |
34 | it('generates `delay` utilities', async () => {
35 | expect(await run('content/delay.html', { scope: 'utilities' })).toIncludeAll([
36 | '.animate-delay-75 { animation-delay: 75ms; }',
37 | '.animate-delay-333 { animation-delay: 333ms; }',
38 | '.animate-delay-\\[666ms\\] { animation-delay: 666ms; }',
39 | ])
40 | })
41 |
42 | it('generates `direction` utilities', async () => {
43 | expect(await run('content/direction.html', { scope: 'utilities' })).toIncludeAll([
44 | '.animate-normal { animation-direction: normal; }',
45 | '.animate-reverse { animation-direction: reverse; }',
46 | ])
47 | })
48 |
49 | it('generates `duration` utilities', async () => {
50 | expect(await run('content/duration.html', { scope: 'utilities' })).toIncludeAll([
51 | '.animate-duration-75 { animation-duration: 75ms; }',
52 | '.animate-duration-333 { animation-duration: 333ms; }',
53 | '.animate-duration-\\[666ms\\] { animation-duration: 666ms; }',
54 | ])
55 | })
56 |
57 | it('generates `fill-mode` utilities', async () => {
58 | expect(await run('content/fill-mode.html', { scope: 'utilities' })).toIncludeAll([
59 | '.animate-fill-both { animation-fill-mode: both; }',
60 | '.animate-fill-none { animation-fill-mode: normal; }',
61 | ])
62 | })
63 |
64 | it('generates `iteration-count` utilities', async () => {
65 | expect(await run('content/iteration-count.html', { scope: 'utilities' })).toIncludeAll([
66 | '.animate-infinite { animation-iteration-count: infinite; }',
67 | '.animate-once { animation-iteration-count: 1; }',
68 | '.animate-iteration-7 { animation-iteration-count: 7; }',
69 | '.animate-iteration-\\[14\\] { animation-iteration-count: 14; }',
70 | ])
71 | })
72 |
73 | it('generates `play-state` utilities', async () => {
74 | expect(await run('content/play-state.html', { scope: 'utilities' })).toIncludeAll([
75 | '.animate-play { animation-play-state: running; }',
76 | '.animate-stop { animation-play-state: paused; }',
77 | ])
78 | })
79 |
80 | it('generates predefined animations for modern import', async () => {
81 | expect(await run('content/predefined-animations.html', { importType: 'modern' })).toIncludeAll([
82 | '.animate-fade { animation: var(--animate-fade); }',
83 | '--animate-fade: fade var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;',
84 | '@keyframes fade { 0% { opacity: 0; } 100% { opacity: 1; }}',
85 | '.animate-spin { animation: var(--animate-spin); }',
86 | '--animate-spin: spin var(--default-animation-duration, 1s) var(--default-animation-timing-function, linear) var(--default-animation-delay, 0s) infinite;',
87 | '@keyframes spin { to { transform: rotate(360deg); }}',
88 | ])
89 | })
90 |
91 | it('generates predefined animations for legacy import', async () => {
92 | expect(await run('content/predefined-animations.html', { importType: 'legacy' })).toIncludeAll([
93 | '.animate-fade { animation: fade var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both; }',
94 | '@keyframes fade { 0% { opacity: 0; } 100% { opacity: 1; }}',
95 | '.animate-spin { animation: spin var(--default-animation-duration, 1s) var(--default-animation-timing-function, linear) var(--default-animation-delay, 0s) infinite; }',
96 | '@keyframes spin { to { transform: rotate(360deg); }}',
97 | ])
98 | })
99 |
100 | it('generates `timing-function` utilities', async () => {
101 | expect(await run('content/timing-function.html', { scope: 'utilities' })).toIncludeAll([
102 | '.animate-ease { animation-timing-function: ease; }',
103 | '.animate-ease-linear { animation-timing-function: linear; }',
104 | '.animate-ease-\\[cubic-bezier\\(1\\,0\\.66\\,0\\.33\\,0\\)\\] { animation-timing-function: cubic-bezier(1,0.66,0.33,0); }',
105 | ])
106 | })
107 | })
108 |
--------------------------------------------------------------------------------
/jest/legacy/index.test.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 | const postcss = require('postcss')
4 | const tailwindcss = require('tailwindcss')
5 |
6 | async function run(file) {
7 | const { currentTestName } = expect.getState()
8 |
9 | const config = {
10 | content: [`./jest/${file}`],
11 | corePlugins: { preflight: false },
12 | plugins: [require('../../src')],
13 | }
14 |
15 | const result = await postcss(tailwindcss(config)).process('@tailwind utilities', {
16 | from: `${path.resolve(__filename)}?test=${currentTestName}`,
17 | })
18 |
19 | return result.css
20 | }
21 |
22 | describe('legacy tailwind', () => {
23 | if (! fs.existsSync(`${__dirname}/node_modules`)) {
24 | throw new Error(`Dependencies in ${__dirname} are missing`)
25 | }
26 |
27 | it('generates `composition` utilities', async () => {
28 | expect(await run('content/composition.html')).toIncludeAll([
29 | '.animate-add { animation-composition: add; }',
30 | '.animate-replace { animation-composition: replace; }',
31 | '.animate-accumulate { animation-composition: accumulate; }',
32 | ])
33 | })
34 |
35 | it('should add `delay` utilities', async () => {
36 | expect(await run('content/delay.html')).toIncludeAll([
37 | '.animate-delay-75 { animation-delay: 75ms; }',
38 | '.animate-delay-\\[666ms\\] { animation-delay: 666ms; }',
39 | ])
40 | })
41 |
42 | it('generates `direction` utilities', async () => {
43 | expect(await run('content/direction.html')).toIncludeAll([
44 | '.animate-normal { animation-direction: normal; }',
45 | '.animate-reverse { animation-direction: reverse; }',
46 | ])
47 | })
48 |
49 | it('generates `duration` utilities', async () => {
50 | expect(await run('content/duration.html')).toIncludeAll([
51 | '.animate-duration-75 { animation-duration: 75ms; }',
52 | '.animate-duration-\\[666ms\\] { animation-duration: 666ms; }',
53 | ])
54 | })
55 |
56 | it('generates `fill-mode` utilities', async () => {
57 | expect(await run('content/fill-mode.html')).toIncludeAll([
58 | '.animate-fill-both { animation-fill-mode: both; }',
59 | '.animate-fill-none { animation-fill-mode: normal; }',
60 | ])
61 | })
62 |
63 | it('generates `iteration-count` utilities', async () => {
64 | expect(await run('content/iteration-count.html')).toIncludeAll([
65 | '.animate-infinite { animation-iteration-count: infinite; }',
66 | '.animate-once { animation-iteration-count: 1; }',
67 | '.animate-iteration-\\[14\\] { animation-iteration-count: 14; }',
68 | ])
69 | })
70 |
71 | it('generates `play-state` utilities', async () => {
72 | expect(await run('content/play-state.html')).toIncludeAll([
73 | '.animate-play { animation-play-state: running; }',
74 | '.animate-stop { animation-play-state: paused; }',
75 | ])
76 | })
77 |
78 | it('generates predefined animations', async () => {
79 | expect(await run('content/predefined-animations.html')).toIncludeAll([
80 | '.animate-fade { animation: fade var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both; }',
81 | '@keyframes fade { 0% { opacity: 0; } 100% { opacity: 1; }}',
82 | '.animate-spin { animation: spin var(--default-animation-duration, 1s) var(--default-animation-timing-function, linear) var(--default-animation-delay, 0s) infinite; }',
83 | '@keyframes spin { to { transform: rotate(360deg); }}',
84 | ])
85 | })
86 |
87 | it('generates `timing-function` utilities', async () => {
88 | expect(await run('content/timing-function.html')).toIncludeAll([
89 | '.animate-ease { animation-timing-function: ease; }',
90 | '.animate-ease-linear { animation-timing-function: linear; }',
91 | '.animate-ease-\\[cubic-bezier\\(1\\2c 0\\.66\\2c 0\\.33\\2c 0\\)\\] { animation-timing-function: cubic-bezier(1,0.66,0.33,0); }',
92 | ])
93 | })
94 | })
95 |
--------------------------------------------------------------------------------
/jest/legacy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "devDependencies": {
4 | "autoprefixer": "^10.4.20",
5 | "tailwindcss": "3.1.0",
6 | "postcss": "^8.5.1"
7 | },
8 | "browserslist": {
9 | "defaults": [
10 | "> 2%"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tailwindcss-animated",
3 | "description": "Extended animation utilities for Tailwind CSS",
4 | "author": "new-data-services",
5 | "license": "MIT",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/new-data-services/tailwindcss-animated.git"
9 | },
10 | "homepage": "https://tailwindcss-animated.com",
11 | "keywords": [
12 | "tailwindcss",
13 | "tailwind",
14 | "plugin",
15 | "animation",
16 | "animated",
17 | "keyframes"
18 | ],
19 | "files": [
20 | "/src"
21 | ],
22 | "main": "src/index.js",
23 | "types": "src/index.d.ts",
24 | "style": "src/index.css",
25 | "scripts": {
26 | "watch": "npm run dev -- -w",
27 | "dev": "npx @tailwindcss/cli -i resources/app.css -o public/app.css",
28 | "test": "jest --setupFilesAfterEnv '/jest/customMatchers.js'",
29 | "lint": "npx eslint {src,jest}/**",
30 | "lint:fix": "npx eslint {src,jest}/** --fix"
31 | },
32 | "peerDependencies": {
33 | "tailwindcss": ">=3.1.0 || >=4.0.0"
34 | },
35 | "devDependencies": {
36 | "@eslint/js": "^9.18.0",
37 | "@tailwindcss/cli": "^4.0.0",
38 | "@tailwindcss/postcss": "^4.0.0",
39 | "eslint": "^9.18.0",
40 | "eslint-plugin-jest": "^28.11.0",
41 | "jest": "^29.7.0",
42 | "postcss": "^8.5.1",
43 | "tailwindcss": "^4.0.0",
44 | "typescript": "^5.7.3"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | tailwindcss-animated
7 |
8 |
9 |
10 |
11 |
12 | Test file for development
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/resources/app.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss" source(none);
2 |
3 | @import "../src/";
4 |
5 | @source "../public/index.html"
6 |
--------------------------------------------------------------------------------
/src/compat/composition.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ addUtilities }) => {
2 | addUtilities({
3 | '.animate-replace': {
4 | 'animation-composition': 'replace',
5 | },
6 | '.animate-add': {
7 | 'animation-composition': 'add',
8 | },
9 | '.animate-accumulate': {
10 | 'animation-composition': 'accumulate',
11 | },
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/compat/delay.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ matchUtilities, theme }) => matchUtilities({
2 | 'animate-delay': value => ({
3 | 'animation-delay': value,
4 | }),
5 | }, {
6 | values: theme('animationDelay'),
7 | })
8 |
--------------------------------------------------------------------------------
/src/compat/direction.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ addUtilities }) => addUtilities({
2 | '.animate-normal': {
3 | 'animation-direction': 'normal',
4 | },
5 | '.animate-reverse': {
6 | 'animation-direction': 'reverse',
7 | },
8 | '.animate-alternate': {
9 | 'animation-direction': 'alternate',
10 | },
11 | '.animate-alternate-reverse': {
12 | 'animation-direction': 'alternate-reverse',
13 | },
14 | })
15 |
--------------------------------------------------------------------------------
/src/compat/duration.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ matchUtilities, theme }) => matchUtilities({
2 | 'animate-duration': value => ({
3 | 'animation-duration': value,
4 | }),
5 | }, {
6 | values: theme('animationDuration'),
7 | })
8 |
--------------------------------------------------------------------------------
/src/compat/fill-mode.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ addUtilities }) => addUtilities({
2 | '.animate-fill-none': {
3 | 'animation-fill-mode': 'normal',
4 | },
5 | '.animate-fill-forwards': {
6 | 'animation-fill-mode': 'forwards',
7 | },
8 | '.animate-fill-backwards': {
9 | 'animation-fill-mode': 'backwards',
10 | },
11 | '.animate-fill-both': {
12 | 'animation-fill-mode': 'both',
13 | },
14 | })
15 |
--------------------------------------------------------------------------------
/src/compat/helper/bare-values.js:
--------------------------------------------------------------------------------
1 | const tailwindPackage = require('tailwindcss/package.json')
2 |
3 | function isLegacyTailwind() {
4 | return tailwindPackage.version?.startsWith('3.')
5 | }
6 |
7 | function isPositiveInteger(value) {
8 | const num = Number(value)
9 |
10 | return Number.isInteger(num) && num >= 0 && String(num) === String(value)
11 | }
12 |
13 | function getBareMilliseconds() {
14 | if (isLegacyTailwind()) {
15 | return {}
16 | }
17 |
18 | return {
19 | __BARE_VALUE__: (value) => {
20 | if (isPositiveInteger(value.value)) {
21 | return `${value.value}ms`
22 | }
23 | }
24 | }
25 | }
26 |
27 | function getBareIntegers() {
28 | if (isLegacyTailwind()) {
29 | return {}
30 | }
31 |
32 | return {
33 | __BARE_VALUE__: (value) => {
34 | if (isPositiveInteger(value.value)) {
35 | return value.value
36 | }
37 | }
38 | }
39 | }
40 |
41 | module.exports = { getBareMilliseconds, getBareIntegers }
42 |
--------------------------------------------------------------------------------
/src/compat/iteration-count.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ addUtilities, matchUtilities, theme }) => {
2 | addUtilities({
3 | '.animate-infinite': {
4 | 'animation-iteration-count': 'infinite',
5 | },
6 | '.animate-once': {
7 | 'animation-iteration-count': '1',
8 | },
9 | '.animate-twice': {
10 | 'animation-iteration-count': '2',
11 | },
12 | '.animate-thrice': {
13 | 'animation-iteration-count': '3',
14 | },
15 | })
16 |
17 | matchUtilities({
18 | 'animate-iteration': value => ({
19 | 'animation-iteration-count': value,
20 | }),
21 | }, {
22 | values: theme('animationIteration'),
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/compat/play-state.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ addUtilities }) => addUtilities({
2 | '.animate-run': {
3 | 'animation-play-state': 'running',
4 | },
5 | '.animate-play': {
6 | 'animation-play-state': 'running',
7 | },
8 | '.animate-stop': {
9 | 'animation-play-state': 'paused',
10 | },
11 | '.animate-pause': {
12 | 'animation-play-state': 'paused',
13 | },
14 | })
15 |
--------------------------------------------------------------------------------
/src/compat/theme.js:
--------------------------------------------------------------------------------
1 | const { getBareMilliseconds, getBareIntegers } = require('./helper/bare-values')
2 |
3 | const bareMilliseconds = getBareMilliseconds()
4 | const bareIntegers = getBareIntegers()
5 |
6 | module.exports = {
7 | extend: {
8 | animationDelay: {
9 | none: '0s',
10 | 0: '0ms',
11 | 75: '75ms',
12 | 100: '100ms',
13 | 150: '150ms',
14 | 200: '200ms',
15 | 300: '300ms',
16 | 500: '500ms',
17 | 700: '700ms',
18 | 1000: '1000ms',
19 | ...bareMilliseconds,
20 | },
21 | animationDuration: {
22 | none: '0s',
23 | 75: '75ms',
24 | 100: '100ms',
25 | 150: '150ms',
26 | 200: '200ms',
27 | 300: '300ms',
28 | 500: '500ms',
29 | 700: '700ms',
30 | 1000: '1000ms',
31 | ...bareMilliseconds,
32 | },
33 | animationTimingFunction: {
34 | DEFAULT: 'ease',
35 | 'linear': 'linear',
36 | 'in': 'cubic-bezier(0.4, 0, 1, 1)',
37 | 'out': 'cubic-bezier(0, 0, 0.2, 1)',
38 | 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
39 | },
40 | animationIteration: {
41 | 'infinite': 'infinite',
42 | 'once': '1',
43 | 'twice': '2',
44 | 'thrice': '3',
45 | ...bareIntegers,
46 | },
47 | animation: {
48 | 'spin': 'spin var(--default-animation-duration, 1s) var(--default-animation-timing-function, linear) var(--default-animation-delay, 0s) infinite',
49 | 'ping': 'ping var(--default-animation-duration, 1s) var(--default-animation-timing-function, cubic-bezier(0, 0, 0.2, 1)) var(--default-animation-delay, 0s) infinite',
50 | 'pulse': 'pulse var(--default-animation-duration, 2s) var(--default-animation-timing-function, cubic-bezier(0.4, 0, 0.6, 1)) var(--default-animation-delay, 0s) infinite',
51 | 'bounce': 'bounce var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) infinite',
52 | 'wiggle': 'wiggle var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
53 | 'wiggle-more': 'wiggle-more var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
54 | 'rotate-y': 'rotate-y var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
55 | 'rotate-x': 'rotate-x var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
56 | 'jump': 'jump var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
57 | 'jump-in': 'jump-in var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
58 | 'jump-out': 'jump-out var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
59 | 'shake': 'shake var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
60 | 'fade': 'fade var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
61 | 'fade-down': 'fade-down var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
62 | 'fade-up': 'fade-up var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
63 | 'fade-left': 'fade-left var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
64 | 'fade-right': 'fade-right var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
65 | 'flip-up': 'flip-up var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
66 | 'flip-down': 'flip-down var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both',
67 | },
68 | keyframes: {
69 | 'wiggle': {
70 | '0%, 100%': {
71 | transform: 'rotate(-3deg)',
72 | },
73 | '50%': {
74 | transform: 'rotate(3deg)',
75 | },
76 | },
77 | 'wiggle-more': {
78 | '0%, 100%': {
79 | transform: 'rotate(-12deg)',
80 | },
81 | '50%': {
82 | transform: 'rotate(12deg)',
83 | },
84 | },
85 | 'rotate-y': {
86 | '0%': {
87 | transform: 'rotateY(360deg)',
88 | },
89 | '100%': {
90 | transform: 'rotateY(0)',
91 | },
92 | },
93 | 'rotate-x': {
94 | '0%': {
95 | transform: 'rotateX(360deg)',
96 | },
97 | '100%': {
98 | transform: 'rotateX(0)',
99 | },
100 | },
101 | 'jump': {
102 | '0%, 100%': {
103 | transform: 'scale(1)',
104 | },
105 | '10%': {
106 | transform: 'scale(0.8)',
107 | },
108 | '50%': {
109 | transform: 'scale(1.2)',
110 | },
111 | },
112 | 'jump-in': {
113 | '0%': {
114 | transform: 'scale(0)',
115 | },
116 | '80%': {
117 | transform: 'scale(1.2)',
118 | },
119 | '100%': {
120 | transform: 'scale(1)',
121 | },
122 | },
123 | 'jump-out': {
124 | '0%': {
125 | transform: 'scale(1)',
126 | },
127 | '20%': {
128 | transform: 'scale(1.2)',
129 | },
130 | '100%': {
131 | transform: 'scale(0)',
132 | },
133 | },
134 | 'shake': {
135 | '0%': {
136 | transform: 'translateX(0rem)',
137 | },
138 | '25%': {
139 | transform: 'translateX(-1rem)',
140 | },
141 | '75%': {
142 | transform: 'translateX(1rem)',
143 | },
144 | '100%': {
145 | transform: 'translateX(0rem)',
146 | },
147 | },
148 | 'fade': {
149 | '0%': {
150 | opacity: '0',
151 | },
152 | '100%': {
153 | opacity: '1',
154 | },
155 | },
156 | 'fade-down': {
157 | '0%': {
158 | opacity: '0',
159 | transform: 'translateY(-2rem)',
160 | },
161 | '100%': {
162 | opacity: '1',
163 | transform: 'translateY(0)',
164 | },
165 | },
166 | 'fade-up': {
167 | '0%': {
168 | opacity: '0',
169 | transform: 'translateY(2rem)',
170 | },
171 | '100%': {
172 | opacity: '1',
173 | transform: 'translateY(0)',
174 | },
175 | },
176 | 'fade-left': {
177 | '0%': {
178 | opacity: '0',
179 | transform: 'translateX(2rem)',
180 | },
181 | '100%': {
182 | opacity: '1',
183 | transform: 'translateX(0)',
184 | },
185 | },
186 | 'fade-right': {
187 | '0%': {
188 | opacity: '0',
189 | transform: 'translateX(-2rem)',
190 | },
191 | '100%': {
192 | opacity: '1',
193 | transform: 'translateX(0)',
194 | },
195 | },
196 | 'flip-up': {
197 | '0%': {
198 | transform: 'rotateX(90deg)',
199 | transformOrigin: 'bottom',
200 | },
201 | '100%': {
202 | transform: 'rotateX(0)',
203 | transformOrigin: 'bottom',
204 | },
205 | },
206 | 'flip-down': {
207 | '0%': {
208 | transform: 'rotateX(-90deg)',
209 | transformOrigin: 'top',
210 | },
211 | '100%': {
212 | transform: 'rotateX(0)',
213 | transformOrigin: 'top',
214 | },
215 | },
216 | },
217 | },
218 | }
219 |
--------------------------------------------------------------------------------
/src/compat/timing-function.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ matchUtilities, theme }) => matchUtilities({
2 | 'animate-ease': value => ({
3 | 'animation-timing-function': value,
4 | }),
5 | }, {
6 | values: theme('animationTimingFunction'),
7 | })
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import './utilities/composition.css';
2 | @import './utilities/delay.css';
3 | @import './utilities/direction.css';
4 | @import './utilities/duration.css';
5 | @import './utilities/fill-mode.css';
6 | @import './utilities/iteration-count.css';
7 | @import './utilities/play-state.css';
8 | @import './utilities/predefined-animations.css';
9 | @import './utilities/timing-function.css';
10 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare const plugin: { handler: () => void }
2 |
3 | export = plugin
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const plugin = require('tailwindcss/plugin')
2 |
3 | module.exports = plugin(api => {
4 | require('./compat/composition')(api)
5 | require('./compat/delay')(api)
6 | require('./compat/direction')(api)
7 | require('./compat/duration')(api)
8 | require('./compat/fill-mode')(api)
9 | require('./compat/iteration-count')(api)
10 | require('./compat/play-state')(api)
11 | require('./compat/timing-function')(api)
12 | }, {
13 | theme: require('./compat/theme'),
14 | })
15 |
--------------------------------------------------------------------------------
/src/utilities/composition.css:
--------------------------------------------------------------------------------
1 | @utility animate-replace {
2 | animation-composition: replace;
3 | }
4 |
5 | @utility animate-add {
6 | animation-composition: add;
7 | }
8 |
9 | @utility animate-accumulate {
10 | animation-composition: accumulate;
11 | }
12 |
--------------------------------------------------------------------------------
/src/utilities/delay.css:
--------------------------------------------------------------------------------
1 | @utility animate-delay-none {
2 | animation-delay: 0s;
3 | }
4 |
5 | @utility animate-delay-* {
6 | animation-delay: --value(integer)ms;
7 | animation-delay: --value([*]);
8 | }
9 |
--------------------------------------------------------------------------------
/src/utilities/direction.css:
--------------------------------------------------------------------------------
1 | @utility animate-normal {
2 | animation-direction: normal;
3 | }
4 |
5 | @utility animate-reverse {
6 | animation-direction: reverse;
7 | }
8 |
9 | @utility animate-alternate {
10 | animation-direction: alternate;
11 | }
12 |
13 | @utility animate-alternate-reverse {
14 | animation-direction: alternate-reverse;
15 | }
16 |
--------------------------------------------------------------------------------
/src/utilities/duration.css:
--------------------------------------------------------------------------------
1 | @utility animate-duration-none {
2 | animation-duration: 0s;
3 | }
4 |
5 | @utility animate-duration-* {
6 | animation-duration: --value(integer)ms;
7 | animation-duration: --value([*]);
8 | }
9 |
--------------------------------------------------------------------------------
/src/utilities/fill-mode.css:
--------------------------------------------------------------------------------
1 | @utility animate-fill-none {
2 | animation-fill-mode: normal;
3 | }
4 |
5 | @utility animate-fill-forwards {
6 | animation-fill-mode: forwards;
7 | }
8 |
9 | @utility animate-fill-backwards {
10 | animation-fill-mode: backwards;
11 | }
12 |
13 | @utility animate-fill-both {
14 | animation-fill-mode: both;
15 | }
16 |
--------------------------------------------------------------------------------
/src/utilities/iteration-count.css:
--------------------------------------------------------------------------------
1 | @utility animate-infinite {
2 | animation-iteration-count: infinite;
3 | }
4 |
5 | @utility animate-once {
6 | animation-iteration-count: 1;
7 | }
8 |
9 | @utility animate-twice {
10 | animation-iteration-count: 2;
11 | }
12 |
13 | @utility animate-thrice {
14 | animation-iteration-count: 3;
15 | }
16 |
17 | @utility animate-iteration-* {
18 | animation-iteration-count: --value(integer, [integer]);
19 | }
20 |
--------------------------------------------------------------------------------
/src/utilities/play-state.css:
--------------------------------------------------------------------------------
1 | @utility animate-run {
2 | animation-play-state: running;
3 | }
4 |
5 | @utility animate-play {
6 | animation-play-state: running;
7 | }
8 |
9 | @utility animate-stop {
10 | animation-play-state: paused;
11 | }
12 |
13 | @utility animate-pause {
14 | animation-play-state: paused;
15 | }
16 |
--------------------------------------------------------------------------------
/src/utilities/predefined-animations.css:
--------------------------------------------------------------------------------
1 | @theme default {
2 | --animate-spin: spin var(--default-animation-duration, 1s) var(--default-animation-timing-function, linear) var(--default-animation-delay, 0s) infinite;
3 | --animate-ping: ping var(--default-animation-duration, 1s) var(--default-animation-timing-function, cubic-bezier(0, 0, 0.2, 1)) var(--default-animation-delay, 0s) infinite;
4 | --animate-pulse: pulse var(--default-animation-duration, 2s) var(--default-animation-timing-function, cubic-bezier(0.4, 0, 0.6, 1)) var(--default-animation-delay, 0s) infinite;
5 | --animate-bounce: bounce var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) infinite;
6 |
7 | --animate-wiggle: wiggle var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
8 | --animate-wiggle-more: wiggle-more var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
9 | --animate-rotate-y: rotate-y var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
10 | --animate-rotate-x: rotate-x var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
11 | --animate-jump: jump var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
12 | --animate-jump-in: jump-in var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
13 | --animate-jump-out: jump-out var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
14 | --animate-shake: shake var(--default-animation-duration, 0.5s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
15 | --animate-fade: fade var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
16 | --animate-fade-down: fade-down var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
17 | --animate-fade-up: fade-up var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
18 | --animate-fade-left: fade-left var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
19 | --animate-fade-right: fade-right var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
20 | --animate-flip-up: flip-up var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
21 | --animate-flip-down: flip-down var(--default-animation-duration, 1s) var(--default-animation-timing-function, ease) var(--default-animation-delay, 0s) both;
22 |
23 | @keyframes wiggle {
24 | 0%,
25 | 100% {
26 | transform: rotate(-3deg);
27 | }
28 | 50% {
29 | transform: rotate(3deg);
30 | }
31 | }
32 |
33 | @keyframes wiggle-more {
34 | 0%,
35 | 100% {
36 | transform: rotate(-12deg);
37 | }
38 | 50% {
39 | transform: rotate(12deg);
40 | }
41 | }
42 |
43 | @keyframes rotate-y {
44 | 0% {
45 | transform: rotateY(360deg);
46 | }
47 | 100% {
48 | transform: rotateY(0);
49 | }
50 | }
51 |
52 | @keyframes rotate-x {
53 | 0% {
54 | transform: rotateX(360deg);
55 | }
56 | 100% {
57 | transform: rotateX(0);
58 | }
59 | }
60 |
61 | @keyframes jump {
62 | 0%,
63 | 100% {
64 | transform: scale(1);
65 | }
66 | 10% {
67 | transform: scale(0.8);
68 | }
69 | 50% {
70 | transform: scale(1.2);
71 | }
72 | }
73 |
74 | @keyframes jump-in {
75 | 0% {
76 | transform: scale(0);
77 | }
78 | 80% {
79 | transform: scale(1.2);
80 | }
81 | 100% {
82 | transform: scale(1);
83 | }
84 | }
85 |
86 | @keyframes jump-out {
87 | 0% {
88 | transform: scale(100%);
89 | }
90 | 20% {
91 | transform: scale(120%);
92 | }
93 | 100% {
94 | transform: scale(0);
95 | }
96 | }
97 |
98 | @keyframes shake {
99 | 0% {
100 | transform: translateX(0);
101 | }
102 | 25% {
103 | transform: translateX(-1rem);
104 | }
105 | 75% {
106 | transform: translateX(1rem);
107 | }
108 | 100% {
109 | transform: translateX(0);
110 | }
111 | }
112 |
113 | @keyframes fade {
114 | 0% {
115 | opacity: 0;
116 | }
117 | 100% {
118 | opacity: 1;
119 | }
120 | }
121 |
122 | @keyframes fade-down {
123 | 0% {
124 | opacity: 0;
125 | transform: translateY(-2rem);
126 | }
127 | 100% {
128 | opacity: 1;
129 | transform: translateY(0);
130 | }
131 | }
132 |
133 | @keyframes fade-up {
134 | 0% {
135 | opacity: 0;
136 | transform: translateY(2rem);
137 | }
138 | 100% {
139 | opacity: 1;
140 | transform: translateY(0);
141 | }
142 | }
143 |
144 | @keyframes fade-left {
145 | 0% {
146 | opacity: 0;
147 | transform: translateX(2rem);
148 | }
149 | 100% {
150 | opacity: 1;
151 | transform: translateX(0);
152 | }
153 | }
154 |
155 | @keyframes fade-right {
156 | 0% {
157 | opacity: 0;
158 | transform: translateX(-2rem);
159 | }
160 | 100% {
161 | opacity: 1;
162 | transform: translateX(0);
163 | }
164 | }
165 |
166 | @keyframes flip-up {
167 | 0% {
168 | transform: rotateX(90deg);
169 | transform-origin: bottom;
170 | }
171 | 100% {
172 | transform: rotateX(0);
173 | transform-origin: bottom;
174 | }
175 | }
176 |
177 | @keyframes flip-down {
178 | 0% {
179 | transform: rotateX(-90deg);
180 | transform-origin: top;
181 | }
182 | 100% {
183 | transform: rotateX(0);
184 | transform-origin: top;
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/utilities/timing-function.css:
--------------------------------------------------------------------------------
1 | @utility animate-ease {
2 | animation-timing-function: ease;
3 | }
4 |
5 | @utility animate-ease-linear {
6 | animation-timing-function: linear;
7 | }
8 |
9 | @utility animate-ease-in {
10 | animation-timing-function: cubic-bezier(0.4, 0, 1, 1);
11 | }
12 |
13 | @utility animate-ease-out {
14 | animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
15 | }
16 |
17 | @utility animate-ease-in-out {
18 | animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
19 | }
20 |
21 | @utility animate-ease-* {
22 | animation-timing-function: --value([*]);
23 | }
24 |
--------------------------------------------------------------------------------