├── pnpm-workspace.yaml
├── web
├── README.md
├── public
│ ├── og.jpg
│ ├── fonts
│ │ ├── Figtree.ttf
│ │ └── GeistMono.ttf
│ └── favicon.svg
├── src
│ ├── env.d.ts
│ ├── components
│ │ ├── icons
│ │ │ ├── moon.astro
│ │ │ ├── sun.astro
│ │ │ ├── system.astro
│ │ │ ├── copy.astro
│ │ │ └── github.astro
│ │ └── ToggleTheme.astro
│ ├── layouts
│ │ └── Layout.astro
│ └── pages
│ │ └── index.astro
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── astro.config.mjs
├── tsconfig.json
├── .gitignore
├── .prettierrc.cjs
├── .eslintrc.cjs
├── tailwind.config.mjs
└── package.json
├── .gitignore
├── eslint.config.js
├── lib
└── imgs
│ └── web.jpg
├── .npmignore
├── .github
├── pull_request_template.md
└── workflows
│ └── ci.yml
├── test
├── utils.js
└── index.test.js
├── package.json
├── LICENSE
├── src
├── index.js
└── theme.js
├── README.es.md
└── README.md
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - '.'
3 | - 'web/**'
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # Web de `@midudev/tailwind-animations`
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | pnpm-lock.yaml
3 | bun.lockb
4 | package-lock.json
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import neostandard from 'neostandard'
2 |
3 | export default neostandard({})
4 |
--------------------------------------------------------------------------------
/lib/imgs/web.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/midudev/tailwind-animations/HEAD/lib/imgs/web.jpg
--------------------------------------------------------------------------------
/web/public/og.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/midudev/tailwind-animations/HEAD/web/public/og.jpg
--------------------------------------------------------------------------------
/web/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/web/public/fonts/Figtree.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/midudev/tailwind-animations/HEAD/web/public/fonts/Figtree.ttf
--------------------------------------------------------------------------------
/web/public/fonts/GeistMono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/midudev/tailwind-animations/HEAD/web/public/fonts/GeistMono.ttf
--------------------------------------------------------------------------------
/web/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github/*
2 | test/*
3 | web/*
4 | web/README.md
5 | lib/imgs/web.jpg
6 | pnpm-workspace.yaml
7 | README.es.md
8 | README.md
--------------------------------------------------------------------------------
/web/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 |
3 | import tailwind from "@astrojs/tailwind";
4 |
5 | // https://astro.build/config
6 | export default defineConfig({
7 | integrations: [tailwind()]
8 | });
--------------------------------------------------------------------------------
/web/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | **What does this PR do?**
2 |
3 | **Why are we doing this?**
4 |
5 | ---
6 | **Test Case(s):**
7 |
8 | **Test Result(s):**
9 |
10 | ---
11 | **Checklist**
12 | - [ ] Tested locally
13 | - [ ] Added new dependencies
14 | - [ ] Updated the docs
15 | - [ ] Added a test
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "paths": {
6 | "@components/*": [
7 | "src/components/*"
8 | ],
9 | "@layouts/*": [
10 | "src/layouts/*"
11 | ]
12 | },
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 | bun.lockb*
16 |
17 | # environment variables
18 | .env
19 | .env.production
20 |
21 | # macOS-specific files
22 | .DS_Store
23 |
--------------------------------------------------------------------------------
/web/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "astro",
5 | "typescript",
6 | ],
7 | "prettier.documentSelectors": ["**/*.astro"],
8 | "[astro]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | },
11 | "editor.formatOnSave": true,
12 | "editor.codeActionsOnSave": {
13 | "source.fixAll": "always"
14 | }
15 | }
--------------------------------------------------------------------------------
/web/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Config} */
2 | module.exports = {
3 | ...require('prettier-config-standard'),
4 | plugins: [
5 | require.resolve('prettier-plugin-astro'),
6 | 'prettier-plugin-tailwindcss'
7 | ],
8 | overrides: [
9 | {
10 | files: '*.astro',
11 | options: {
12 | parser: 'astro'
13 | }
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/web/src/components/icons/moon.astro:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web/src/components/icons/sun.astro:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/web/src/components/icons/system.astro:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/web/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import("eslint").Linter.Config} */
2 | module.exports = {
3 | extends: ['plugin:astro/recommended', 'prettier'],
4 | parser: '@typescript-eslint/parser',
5 | parserOptions: {
6 | tsconfigRootDir: __dirname,
7 | sourceType: 'module',
8 | ecmaVersion: 'latest'
9 | },
10 | overrides: [
11 | {
12 | files: ['*.astro'],
13 | parser: 'astro-eslint-parser',
14 | parserOptions: {
15 | parser: '@typescript-eslint/parser',
16 | extraFileExtensions: ['.astro']
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/web/src/components/icons/copy.astro:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/web/tailwind.config.mjs:
--------------------------------------------------------------------------------
1 | import animations from '../src/index.js'
2 | import theme from '../src/theme.js'
3 |
4 | const { animation } = theme
5 |
6 | const safelist = Object.keys(animation).map((key) => `animate-${key}`)
7 |
8 | /** @type {import('tailwindcss').Config} */
9 | export default {
10 | darkMode: 'class',
11 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
12 | theme: {
13 | extend: {
14 | fontFamily: {
15 | sans: ['Figtree', 'sans-serif'],
16 | mono: ['Geist Mono', 'monospace']
17 | }
18 | }
19 | },
20 | plugins: [animations],
21 | safelist
22 | }
23 |
--------------------------------------------------------------------------------
/web/src/components/icons/github.astro:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | const props = Astro.props
4 | ---
5 |
6 |
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | import tailwindcss from 'tailwindcss'
2 | import postcss from 'postcss'
3 | import minify from '@csstools/postcss-minify'
4 | import animationsPlugin from '../src/index.js'
5 |
6 | const TAILWIND_BASE = '@tailwind utilities;'
7 |
8 | export function generatePluginCSS (options = {}) {
9 | const { inline = '', content = '' } = options
10 |
11 | return postcss([
12 | minify(),
13 | tailwindcss({
14 | plugins: [animationsPlugin],
15 | content: [{ raw: content }]
16 | })
17 | ])
18 | .process(`${TAILWIND_BASE} ${inline}`, {
19 | from: undefined
20 | })
21 | .then(result => result.css)
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | types: [opened, synchronize, reopened]
9 |
10 | jobs:
11 | test:
12 | runs-on: ubuntu-22.04
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4
16 | with:
17 | fetch-depth: 0
18 | - name: Install Bun
19 | uses: oven-sh/setup-bun@v1
20 | with:
21 | bun-version: latest
22 | - name: Install dependencies
23 | run: bun install
24 | - name: Run Linter
25 | run: bun run lint
26 | - name: Run tests
27 | run: bun run test:ci
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@midudev/tailwind-animations",
3 | "version": "0.2.0",
4 | "description": "Tailwind CSS plugin to add animations to your website",
5 | "main": "./src/index.js",
6 | "type": "module",
7 | "scripts": {
8 | "lint": "eslint ./src",
9 | "lint:fix": "eslint --fix ./src",
10 | "test": "vitest",
11 | "test:ci": "vitest run"
12 | },
13 | "keywords": [],
14 | "author": "Miguel Ángel Durán (@midudev) & su maravillosa comunidad",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "eslint": "9.21.0",
18 | "postcss": "8.5.3",
19 | "neostandard": "0.12.1",
20 | "tailwindcss": "3.4.17",
21 | "@csstools/postcss-minify": "2.0.3",
22 | "vitest": "3.0.8"
23 | },
24 | "peerDependencies": {
25 | "tailwindcss": "^3.0.0 || ^4.0.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/web/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro dev",
8 | "build": "astro check && astro build",
9 | "preview": "astro preview",
10 | "astro": "astro",
11 | "lint": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\" && eslint --fix \"src/**/*.{js,ts,jsx,tsx,astro}\""
12 | },
13 | "dependencies": {
14 | "@astrojs/check": "^0.4.1",
15 | "@astrojs/tailwind": "^5.1.0",
16 | "astro": "^4.3.3",
17 | "tailwindcss": "^3.4.1",
18 | "typescript": "^5.3.3",
19 | "wc-toast": "1.3.1"
20 | },
21 | "devDependencies": {
22 | "@typescript-eslint/parser": "^6.21.0",
23 | "eslint": "^8.56.0",
24 | "eslint-config-prettier": "9.1.0",
25 | "eslint-plugin-astro": "^0.31.4",
26 | "eslint-plugin-jsx-a11y": "^6.8.0",
27 | "prettier": "^3.2.5",
28 | "prettier-config-standard": "^7.0.0",
29 | "prettier-plugin-astro": "^0.13.0",
30 | "prettier-plugin-tailwindcss": "0.5.11"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Miguel Ángel Durán
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 |
--------------------------------------------------------------------------------
/web/src/layouts/Layout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | interface Props {
3 | title: string
4 | }
5 |
6 | const { title } = Astro.props
7 | ---
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 | {title}
28 |
29 |
32 |
33 |
34 |
35 |
36 |
47 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import createPlugin from 'tailwindcss/plugin.js'
2 | import theme from './theme.js'
3 |
4 | /** @type {import('tailwindcss/types/config').PluginCreator} */
5 | const pluginCreator = api => {
6 | const { theme, matchUtilities, addUtilities } = api
7 |
8 | // Predefined animations in same element
9 | const singleTimeline = value => {
10 | let customNameTimeline = value
11 | if (value.startsWith('var(')) customNameTimeline = value.slice(4, -1)
12 |
13 | return customNameTimeline
14 | }
15 |
16 | const dynamicUtils = {
17 | 'animate-delay': { css: 'animation-delay', values: theme('animationDelay') },
18 | 'animate-duration': { css: 'animation-duration', values: theme('animationDuration') },
19 | 'animate-iteration-count': { css: 'animation-iteration-count', values: theme('animationIterationCount') },
20 | 'animate-fill-mode': { css: 'animation-fill-mode', values: theme('animationFillMode') },
21 | 'animate-bezier': { css: 'animation-timing-function', values: theme('animationCubicBezier') },
22 | 'animate-steps': { css: 'animation-timing-function', values: theme('animationSteps'), generateValue: value => `steps(${value})` },
23 | 'animate-range': { css: 'animation-range', values: theme('animationRange'), generateValue: value => value },
24 | timeline: { css: 'animation-timeline', values: theme('timeline'), generateValue: value => singleTimeline(value) },
25 | 'scroll-timeline': { css: 'scroll-timeline-name', values: theme('scrollTimeline'), generateValue: (value) => singleTimeline(value) },
26 | 'view-timeline': { css: 'view-timeline-name', values: theme('viewTimeline'), generateValue: (value) => singleTimeline(value) },
27 | 'scroll-timeline-axis': { css: 'scroll-timeline-axis', values: theme('scrollTimelineAxis') },
28 | 'view-timeline-axis': { css: 'view-timeline-axis', values: theme('viewTimelineAxis') },
29 | 'scroll-animate': { css: 'scroll-timeline-name', values: theme('scrollTimeline'), generateValue: (value) => `${singleTimeline(value)};\n animation-timeline: ${singleTimeline(value)}` },
30 | 'view-animate': { css: 'view-timeline-name', values: theme('viewTimeline'), generateValue: (value) => `${singleTimeline(value)};\n animation-timeline: ${singleTimeline(value)}` }
31 | }
32 |
33 | Object.entries(dynamicUtils).forEach(([name, { css, values, generateValue }]) => {
34 | matchUtilities({
35 | [name]: value => ({
36 | [css]: generateValue ? generateValue(value) : value
37 | })
38 | }, {
39 | values
40 | })
41 | })
42 |
43 | addUtilities({
44 | '.animate-ease': {
45 | 'animation-timing-function': 'ease'
46 | },
47 | '.animate-ease-in': {
48 | 'animation-timing-function': 'ease-in'
49 | },
50 | '.animate-ease-out': {
51 | 'animation-timing-function': 'ease-out'
52 | },
53 | '.animate-ease-in-out': {
54 | 'animation-timing-function': 'ease-in-out'
55 | },
56 | '.animate-linear': {
57 | 'animation-timing-function': 'linear'
58 | },
59 | '.animate-direction-normal': {
60 | 'animation-direction': 'normal'
61 | },
62 | '.animate-direction-reverse': {
63 | 'animation-direction': 'reverse'
64 | },
65 | '.animate-direction-alternate': {
66 | 'animation-direction': 'alternate'
67 | },
68 | '.animate-direction-alternate-reverse': {
69 | 'animation-direction': 'alternate-reverse'
70 | },
71 | '.animate-play-running': {
72 | 'animation-play-state': 'running'
73 | },
74 | '.animate-play-paused': {
75 | 'animation-play-state': 'paused'
76 | }
77 | })
78 | }
79 |
80 | /** @type {import('tailwindcss/types/config').Config} */
81 | const pluginConfig = { theme }
82 |
83 | export default createPlugin(pluginCreator, pluginConfig)
84 |
--------------------------------------------------------------------------------
/web/src/components/ToggleTheme.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import SunIcon from "./icons/sun.astro"
3 | import MoonIcon from "./icons/moon.astro"
4 | import SystemIcon from "./icons/system.astro"
5 |
6 | const THEMES = ["Light", "Dark", "System"]
7 | ---
8 |
9 |
10 |
14 | Choose a theme
15 |
16 |
19 |
22 |
23 |
37 |
38 |
39 |
56 |
57 |
113 |
--------------------------------------------------------------------------------
/README.es.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Awesome Tailwind Animations
4 |
5 | [](./README.md)
6 | [](./README.es.md)
7 |
8 | 
9 | 
10 | 
11 | 
12 | 
13 |
14 | 
15 |
16 | 
18 | 
19 |
20 | Obten animaciones de CSS con una sola clase de Tailwind!
21 |
22 | Visita la [web](https://tailwindcss-animations.vercel.app/) para obtener más
23 | información.
24 |
25 |
26 |
27 | ## Instalación
28 |
29 | Instala el paquete con tu gestor de paquetes favorito:
30 |
31 | ```sh
32 | $ npm install @midudev/tailwind-animations
33 | $ pnpm add @midudev/tailwind-animations
34 | $ yarn add @midudev/tailwind-animations
35 | ```
36 |
37 | Usa el plugin en tu configuración de Tailwind:
38 |
39 | ```js
40 |
41 | // tailwind.config.mjs
42 | import animations from '@midudev/tailwind-animations'
43 |
44 | export default {
45 | // resto de opciones
46 | plugins: [
47 | animations
48 | ],
49 | }
50 |
51 | ```
52 |
53 | ## Uso
54 |
55 | Este plugin trae varias clases de utilidad así como varias animaciones CSS listas para usar. Aquí tienes algunos ejemplos simples:
56 |
57 | ```html
58 |
59 | Fade in box
60 |
61 |
62 |
63 | Slow animation after 300ms to slide in from bottom
64 |
65 | ```
66 |
67 | ### Animates Timeline
68 |
69 | Este plugin también trae una clase de utilidad para animar elementos basados en su posición en la ventana. Puedes usar la clase `view-animate-single` o `view-animate-[animation]` para poder generar cualquier nombre a tu timeline.
70 |
71 | ```html
72 |
73 |
Content
74 |
75 |
76 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
77 | tempor incididunt ut labore et dolore magna aliqua. Risus quis varius quam
78 | quisque id. Et ligula ullamcorper malesuada proin libero nunc consequat
79 | interdum varius. Elit ullamcorper dignissim cras tincidunt lobortis feugiat
80 | vivamus at augue.
81 |
82 |
83 |
84 | Dolor sed viverra ipsum nunc aliquet. Sed sed risus pretium quam vulputate
85 | dignissim. Tortor aliquam nulla facilisi cras. A erat nam at lectus urna
86 | duis convallis convallis. Nibh ipsum consequat nisl vel pretium lectus.
87 | Sagittis aliquam malesuada bibendum arcu vitae elementum. Malesuada bibendum
88 | arcu vitae elementum curabitur vitae nunc sed velit.
89 |
90 |
91 |
93 |
94 |
95 |
96 | Adipiscing enim eu turpis egestas pretium aenean pharetra magna ac. Arcu
97 | cursus vitae congue mauris rhoncus aenean vel. Sit amet cursus sit amet
98 | dictum. Augue neque gravida in fermentum et. Gravida rutrum quisque non
99 | tellus orci ac auctor augue mauris. Risus quis varius quam quisque id diam
100 | vel quam elementum. Nibh praesent tristique magna sit amet purus gravida
101 | quis. Duis ultricies lacus sed turpis tincidunt id aliquet. In egestas erat
102 | imperdiet sed euismod nisi. Eget egestas purus viverra accumsan in nisl nisi
103 | scelerisque. Netus et malesuada fames ac.
104 |
105 |
106 | ```
107 | > Ejemplo extraido de [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/view-timeline)
108 |
109 | ## Contribuidores
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Awesome Tailwind Animations
4 |
5 | [](./README.md)
6 | [](./README.es.md)
7 |
8 | 
9 | 
10 | 
11 | 
12 | 
13 |
14 | 
15 |
16 | 
18 | 
19 |
20 | Get your animations easily done with only Tailwind CSS classes.
21 |
22 | Visit the [website](https://tailwindcss-animations.vercel.app/) to get more information.
23 |
24 |
25 | ## Installation :book:
26 |
27 | #### Package install
28 |
29 | > Install the package with your favorite package manager:
30 |
31 | - npm
32 | ```bash
33 | npm install @midudev/tailwind-animations
34 | ```
35 | - pnpm
36 | ```bash
37 | pnpm install @midudev/tailwind-animations
38 | ```
39 | - yarn
40 | ```bash
41 | yarn add @midudev/tailwind-animations
42 | ```
43 |
44 | #### Plugin Implementation
45 | > Use the plugin in your Tailwind CSS project:
46 |
47 | ```js
48 | // tailwind.config.mjs (for Tailwind CSS 3.*)
49 | import animations from '@midudev/tailwind-animations'
50 |
51 | export default {
52 | // rest of the options
53 | plugins: [
54 | animations
55 | ],
56 | }
57 | ```
58 |
59 | ```css
60 | /* globals.css (for Tailwind CSS 4.*) */
61 | @import "tailwindcss";
62 |
63 | @plugin '@midudev/tailwind-animations';
64 | ```
65 |
66 | ## Usage :gear:
67 |
68 | #### Example
69 |
70 | > Here are some simple examples of how to use this plugin and its animations:
71 |
72 | ```html
73 |
74 | Fade in box
75 |
76 |
77 |
78 | Slow animation after 300ms to slide in from bottom
79 |
80 | ```
81 |
82 | ### Animates Timeline
83 |
84 | This plugin also brings a utility class to animate elements based on their position in the window. You can use the class `view-animate-single` or `view-animate-[animation]` to generate any name for your timeline.
85 |
86 | ```html
87 |
88 |
Content
89 |
90 |
91 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
92 | tempor incididunt ut labore et dolore magna aliqua. Risus quis varius quam
93 | quisque id. Et ligula ullamcorper malesuada proin libero nunc consequat
94 | interdum varius. Elit ullamcorper dignissim cras tincidunt lobortis feugiat
95 | vivamus at augue.
96 |
97 |
98 |
99 | Dolor sed viverra ipsum nunc aliquet. Sed sed risus pretium quam vulputate
100 | dignissim. Tortor aliquam nulla facilisi cras. A erat nam at lectus urna
101 | duis convallis convallis. Nibh ipsum consequat nisl vel pretium lectus.
102 | Sagittis aliquam malesuada bibendum arcu vitae elementum. Malesuada bibendum
103 | arcu vitae elementum curabitur vitae nunc sed velit.
104 |
105 |
106 |
108 |
109 |
110 |
111 | Adipiscing enim eu turpis egestas pretium aenean pharetra magna ac. Arcu
112 | cursus vitae congue mauris rhoncus aenean vel. Sit amet cursus sit amet
113 | dictum. Augue neque gravida in fermentum et. Gravida rutrum quisque non
114 | tellus orci ac auctor augue mauris. Risus quis varius quam quisque id diam
115 | vel quam elementum. Nibh praesent tristique magna sit amet purus gravida
116 | quis. Duis ultricies lacus sed turpis tincidunt id aliquet. In egestas erat
117 | imperdiet sed euismod nisi. Eget egestas purus viverra accumsan in nisl nisi
118 | scelerisque. Netus et malesuada fames ac.
119 |
120 |
121 | ```
122 | > Example extracted from [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/view-timeline)
123 |
124 | ## Contributors 👑
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | import { generatePluginCSS } from './utils.js'
2 | import { describe, it, expect } from 'vitest'
3 |
4 | describe('tailwindcss-animations plugins', () => {
5 | it('use a predefined animation', async () => {
6 | const css = await generatePluginCSS({
7 | content: 'Hello
'
8 | })
9 | expect(css).toMatch('@keyframes zoom-in{0%{opacity:0;transform:scale(.5)}100%{opacity:1;transform:scale(1)}}.animate-zoom-in{animation:zoom-in 0.6s ease-out both}')
10 | })
11 |
12 | it('use fade in up animation', async () => {
13 | const css = await generatePluginCSS({
14 | content: 'Hello
'
15 | })
16 | expect(css).toMatch('@keyframes fade-in-up{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.animate-fade-in-up{animation:fade-in-up 0.6s ease-in-out both}')
17 | })
18 |
19 | it('use a predefined animation delay', async () => {
20 | const css = await generatePluginCSS({
21 | content: 'Hello
'
22 | })
23 |
24 | expect(css).toMatch('.animate-delay-100{animation-delay:100ms}')
25 | })
26 |
27 | it('use a custom animation delay', async () => {
28 | const css = await generatePluginCSS({
29 | content: 'Hello
'
30 | })
31 |
32 | expect(css).toMatch('.animate-delay-\\[777ms\\]{animation-delay:777ms}')
33 | })
34 |
35 | it('use a predefined animation duration', async () => {
36 | const css = await generatePluginCSS({
37 | content: 'Hello
'
38 | })
39 |
40 | expect(css).toMatch('.animate-duration-100{animation-duration:100ms}')
41 | })
42 |
43 | it('use a predefined named animation duration', async () => {
44 | const css = await generatePluginCSS({
45 | content: 'Hello
'
46 | })
47 |
48 | expect(css).toMatch('.animate-duration-faster{animation-duration:100ms}')
49 | })
50 |
51 | it('use a custom animation duration', async () => {
52 | const css = await generatePluginCSS({
53 | content: 'Hello
'
54 | })
55 |
56 | expect(css).toMatch('.animate-duration-\\[777ms\\]{animation-duration:777ms}')
57 | })
58 |
59 | it('use a timing function animation', async () => {
60 | const css = await generatePluginCSS({
61 | content: 'Hello
'
62 | })
63 |
64 | expect(css).toMatch('.animate-linear{animation-timing-function:linear}')
65 | })
66 |
67 | it('use a bezier curve as a timing function animation', async () => {
68 | const css = await generatePluginCSS({
69 | content: 'Hello
'
70 | })
71 |
72 | expect(css).toMatch('.animate-bezier-sine-in{animation-timing-function:cubic-bezier(0.12,0,0.39,0)}')
73 | })
74 |
75 | it('use a custom bezier curve as a timing function animation', async () => {
76 | const css = await generatePluginCSS({
77 | content: 'Hello
'
78 | })
79 |
80 | expect(css).toMatch(
81 | '.animate-bezier-\\[cubic-bezier\\(0\\2c 0\\2c 0\\2c 0\\)\\]{animation-timing-function:cubic-bezier(0,0,0,0)}'
82 | )
83 | })
84 |
85 | it('use a custom animation iteration count', async () => {
86 | const css = await generatePluginCSS({
87 | content: 'Hello
'
88 | })
89 |
90 | expect(css).toMatch('.animate-iteration-count-twice{animation-iteration-count:2}')
91 | })
92 |
93 | it('use a custom animation iteration count with an arbitrary value', async () => {
94 | const css = await generatePluginCSS({
95 | content: 'Hello
'
96 | })
97 |
98 | expect(css).toMatch('.animate-iteration-count-\\[10\\]{animation-iteration-count:10}')
99 | })
100 |
101 | it('use a custom animation direction', async () => {
102 | const css = await generatePluginCSS({
103 | content: 'Hello
'
104 | })
105 |
106 | expect(css).toMatch('.animate-direction-reverse{animation-direction:reverse}')
107 | })
108 |
109 | it('use a fill mode animation', async () => {
110 | const css = await generatePluginCSS({
111 | content: 'Hello
'
112 | })
113 |
114 | expect(css).toMatch('.animate-fill-mode-forwards{animation-fill-mode:forwards}')
115 | })
116 |
117 | it('use not custom animation steps', async () => {
118 | const css = await generatePluginCSS({
119 | content: 'Hello
'
120 | })
121 |
122 | expect(css).toMatch('.animate-steps-retro{animation-timing-function:steps(8)}')
123 | })
124 | it('use a custom animation steps', async () => {
125 | const css = await generatePluginCSS({
126 | content: 'Hello
'
127 | })
128 |
129 | expect(css).toMatch('.animate-steps-\\[33\\]{animation-timing-function:steps(33)}')
130 | })
131 |
132 | it('use a play state animation play', async () => {
133 | const css = await generatePluginCSS({
134 | content: 'Hello
'
135 | })
136 |
137 | expect(css).toMatch('.animate-play-paused{animation-play-state:paused}')
138 | })
139 |
140 | it('use a animation timeline none or auto', async () => {
141 | const css = await generatePluginCSS({
142 | content: 'Hello
'
143 | })
144 |
145 | expect(css).toMatch('.timeline-none{animation-timeline:none}')
146 | })
147 |
148 | it('use a animation timeline single', async () => {
149 | const css = await generatePluginCSS({
150 | content: 'Hello
'
151 | })
152 |
153 | expect(css).toMatch('.timeline-single{animation-timeline:--single-timeline}')
154 | })
155 |
156 | it('use a animation timeline scroll', async () => {
157 | const css = await generatePluginCSS({
158 | content: 'Hello
'
159 | })
160 |
161 | expect(css).toMatch('.timeline-scroll{animation-timeline:scroll()}')
162 | })
163 |
164 | it('use a animation timeline scroll custom', async () => {
165 | const css = await generatePluginCSS({
166 | content: 'Hello
'
167 | })
168 |
169 | expect(css).toMatch('.timeline-\\[scroll\\(20\\%\\)\\]{animation-timeline:scroll(20%)}')
170 | })
171 |
172 | it('use a animation timeline custom name', async () => {
173 | const css = await generatePluginCSS({
174 | content: 'Hello
'
175 | })
176 |
177 | expect(css).toMatch('.timeline-\\[--test\\]{animation-timeline:--test}')
178 | })
179 |
180 | it('use a scroll timeline single', async () => {
181 | const css = await generatePluginCSS({
182 | content: 'Hello
'
183 | })
184 |
185 | expect(css).toMatch('.scroll-timeline-single{scroll-timeline-name:--single-timeline}')
186 | })
187 |
188 | it('use a view timeline single', async () => {
189 | const css = await generatePluginCSS({
190 | content: 'Hello
'
191 | })
192 |
193 | expect(css).toMatch('.view-timeline-single{view-timeline-name:--single-timeline}')
194 | })
195 |
196 | it('use a view timeline custom name', async () => {
197 | const css = await generatePluginCSS({
198 | content: 'Hello
'
199 | })
200 |
201 | expect(css).toMatch('.view-timeline-\\[--test-view\\]{view-timeline-name:--test-view}')
202 | })
203 |
204 | it('use a timeline range', async () => {
205 | const css = await generatePluginCSS({
206 | content: 'Hello
'
207 | })
208 |
209 | expect(css).toMatch('.animate-range-cover{animation-range:cover}')
210 | })
211 |
212 | it('use a timeline range porcentual', async () => {
213 | const css = await generatePluginCSS({
214 | content: 'Hello
'
215 | })
216 |
217 | expect(css).toMatch('.animate-range-moderate{animation-range:20% 80%}')
218 | })
219 |
220 | it('use a timeline range custom', async () => {
221 | const css = await generatePluginCSS({
222 | content: 'Hello
'
223 | })
224 |
225 | expect(css).toMatch('.animate-range-\\[12\\%_65\\%\\]{animation-range:12% 65%}')
226 | })
227 |
228 | it('use a scroll timeline axis', async () => {
229 | const css = await generatePluginCSS({
230 | content: 'Hello
'
231 | })
232 |
233 | expect(css).toMatch('.scroll-timeline-axis-block{scroll-timeline-axis:block}')
234 | })
235 |
236 | it('use a view timeline axis', async () => {
237 | const css = await generatePluginCSS({
238 | content: 'Hello
'
239 | })
240 |
241 | expect(css).toMatch('.view-timeline-axis-y{view-timeline-axis:y}')
242 | })
243 |
244 | it('use view animation timeline in same element', async () => {
245 | const css = await generatePluginCSS({
246 | content: 'Hello
'
247 | })
248 |
249 | expect(css).toMatch('.view-animate-single{view-timeline-name:--single-timeline; animation-timeline: --single-timeline}')
250 | })
251 |
252 | it('use scroll animation timeline in same element', async () => {
253 | const css = await generatePluginCSS({
254 | content: 'Hello
'
255 | })
256 |
257 | expect(css).toMatch('.scroll-animate-single{scroll-timeline-name:--single-timeline; animation-timeline: --single-timeline}')
258 | })
259 |
260 | it('use scroll animation timeline in same element with custom name', async () => {
261 | const css = await generatePluginCSS({
262 | content: 'Hello
'
263 | })
264 |
265 | expect(css).toMatch('.scroll-animate-\\[--test-timeline-scroll\\]{scroll-timeline-name:--test-timeline-scroll; animation-timeline: --test-timeline-scroll}')
266 | })
267 | })
268 |
--------------------------------------------------------------------------------
/web/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from '@layouts/Layout.astro'
3 |
4 | import pkg from '../../../package.json'
5 | import theme from '../../../src/theme.js'
6 |
7 | import CopyIcon from '@components/icons/copy.astro'
8 | import { Code } from 'astro:components'
9 | import Github from '@components/icons/github.astro'
10 | import ToggleTheme from '@components/ToggleTheme.astro'
11 |
12 | const { animation, animationDuration, animationSteps, animationDelay } = theme
13 | const { version } = pkg
14 | ---
15 |
16 |
19 |
20 |
21 |
24 |
40 |
41 | tailwind css animations
42 |
43 |
44 | the plugin that you need! =)
45 |
46 |
47 |
1. Install the dependency
50 |
53 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
2. Add to your configuration
69 |
80 |
81 |
82 |
83 |
152 |
153 |
156 | {
157 | Object.keys(animation).map((animationKey) => {
158 | return (
159 |
163 |
164 |
165 |
{animationKey}
166 |
167 |
171 |
172 |
173 |
174 | )
175 | })
176 | }
177 |
178 |
179 |
182 |
183 | Based on Julien Thibeaut Tailwind animations
189 |
190 |
191 |
192 | Created by Midudev
198 | and his precious
204 | community
206 | ❤
207 |
208 |
209 |
210 |
211 |
212 |
326 |
327 |
332 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | export default {
2 | animation: {
3 | 'blurred-fade-in': 'blurred-fade-in 0.9s ease-in-out both',
4 | 'fade-in': 'fade-in 0.6s ease-in both',
5 | 'fade-out': 'fade-out 0.6s ease-out both',
6 | 'slide-in-top': 'slide-in-top 0.6s ease-out both',
7 | 'slide-in-bottom': 'slide-in-bottom 0.6s ease-out both',
8 | 'slide-out-top': 'slide-out-top 0.6s ease-out both',
9 | 'slide-out-bottom': 'slide-out-bottom 0.6s ease-out both',
10 | 'zoom-in': 'zoom-in 0.6s ease-out both',
11 | 'zoom-out': 'zoom-out 0.6s ease-out both',
12 | 'rotate-90': 'rotate-90 1s ease-in-out both',
13 | 'rotate-180': 'rotate-180 1s ease-in-out both',
14 | 'rotate-360': 'rotate-360 1s linear both',
15 | 'flip-horizontal': 'flip-horizontal 1s ease-in-out both',
16 | 'flip-vertical': 'flip-vertical 1s ease-in-out both',
17 | bouncing: 'bouncing 1s ease-in-out both',
18 | swing: 'swing 1s ease-in-out both',
19 | wobble: 'wobble 1s ease-in-out both',
20 | pulsing: 'pulsing 1s ease-in-out both',
21 | shake: 'shake 0.5s ease-in-out both',
22 | tada: 'tada 1s ease-in-out both',
23 | jump: 'jump 1s ease-in-out both',
24 | hang: 'hang 1s ease-in-out both',
25 | 'roll-in': 'roll-in 1s ease-in-out both',
26 | 'roll-out': 'roll-out 1s ease-in-out both',
27 | float: 'float 1s ease-in-out both',
28 | sink: 'sink 1s ease-in-out both',
29 | flash: 'flash 1s ease-in-out both',
30 | jiggle: 'jiggle 0.5s ease-in-out both',
31 | 'rubber-band': 'rubber-band 1s ease-in-out both',
32 | scale: 'scale 0.6s ease-out both',
33 | 'slide-in-left': 'slide-in-left 0.6s ease-out both',
34 | 'slide-in-right': 'slide-in-right 0.6s ease-out both',
35 | 'slide-out-left': 'slide-out-left 0.6s ease-out both',
36 | 'slide-out-right': 'slide-out-right 0.6s ease-out both',
37 | 'spin-clockwise': 'spin-clockwise 0.6s linear both',
38 | 'spin-counter-clockwise': 'spin-counter-clockwise 0.6s linear both',
39 | 'flip-x': 'flip-x 0.6s ease-out both',
40 | 'flip-y': 'flip-y 0.6s ease-out both',
41 | blink: 'blink 0.5s both',
42 | pop: 'pop 0.6s ease-out both',
43 | 'expand-horizontally': 'expand-horizontally 0.6s ease-out both',
44 | 'contract-horizontally': 'contract-horizontally 0.6s ease-out both',
45 | 'expand-vertically': 'expand-vertically 0.6s ease-out both',
46 | 'contract-vertically': 'contract-vertically 0.6s ease-out both',
47 | 'fade-in-up': 'fade-in-up 0.6s ease-in-out both',
48 | 'fade-in-down': 'fade-in-down 0.6s ease-in-out both',
49 | 'fade-in-left': 'fade-in-left 0.6s ease-in-out both',
50 | 'fade-in-right': 'fade-in-right 0.6s ease-in-out both',
51 | 'fade-out-up': 'fade-out-up 0.6s ease-out both',
52 | 'fade-out-down': 'fade-out-down 0.6s ease-out both',
53 | 'fade-out-left': 'fade-out-left 0.6s ease-out both',
54 | 'fade-out-right': 'fade-out-right 0.6s ease-out both',
55 | sway: 'sway 0.6s ease-out both',
56 | 'flip-in-x': 'flip-in-x 0.6s ease-out both',
57 | 'flip-in-y': 'flip-in-y 0.6s ease-out both',
58 | 'flip-out-x': 'flip-out-x 0.6s ease-out both',
59 | 'flip-out-y': 'flip-out-y 0.6s ease-out both',
60 | 'rotate-in': 'rotate-in 0.6s ease-out both',
61 | 'rotate-out': 'rotate-out 0.6s ease-out both',
62 | 'slide-rotate-in': 'slide-rotate-in 0.6s ease-out both',
63 | 'slide-rotate-out': 'slide-rotate-out 0.6s ease-out both',
64 | heartbeat: 'heartbeat 0.6s ease-out both',
65 | 'horizontal-vibration': 'horizontal-vibration 0.3s linear infinite both',
66 | 'rotational-wave': 'rotational-wave 2s ease-in-out infinite both',
67 | skew: 'skew 0.5s ease-in-out both',
68 | 'skew-right': 'skew-right 0.5s ease-in-out both',
69 | 'vertical-bounce': 'vertical-bounce 0.6s ease-in-out both',
70 | 'horizontal-bounce': 'horizontal-bounce 0.6s ease-in-out both',
71 | tilt: 'tilt 0.6s ease-in-out both',
72 | squeeze: 'squeeze 0.6s ease-in-out both',
73 | 'slide-up-fade': 'slide-up-fade 0.6s ease-out both',
74 | 'bounce-fade-in': 'bounce-fade-in 0.6s ease-out both',
75 | 'swing-drop-in': 'swing-drop-in 0.6s ease-out both',
76 | 'pulse-fade-in': 'pulse-fade-in 0.6s ease-out both',
77 | 'impulse-rotation-right': 'impulse-rotation-right 1s ease-in-out both',
78 | 'impulse-rotation-left': 'impulse-rotation-left 1s ease-in-out both',
79 | dancing: 'dancing 1s ease-in-out both',
80 | pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
81 | jelly: 'jelly 1s ease-out forwards'
82 | },
83 | keyframes: {
84 | 'fade-in': {
85 | '0%': { opacity: '0' },
86 | '100%': { opacity: '1' }
87 | },
88 | 'fade-out': {
89 | '0%': { opacity: '1' },
90 | '100%': { opacity: '0' }
91 | },
92 | 'slide-in-top': {
93 | '0%': { transform: 'translateY(-20px)' },
94 | '100%': { transform: 'translateY(0)' }
95 | },
96 | 'slide-in-bottom': {
97 | '0%': { transform: 'translateY(20px)' },
98 | '100%': { transform: 'translateY(0)' }
99 | },
100 | 'slide-out-top': {
101 | '0%': { transform: 'translateY(0)' },
102 | '100%': { transform: 'translateY(-20px)' }
103 | },
104 | 'slide-out-bottom': {
105 | '0%': { transform: 'translateY(0)' },
106 | '100%': { transform: 'translateY(20px)' }
107 | },
108 | 'zoom-in': {
109 | '0%': { opacity: '0', transform: 'scale(.5)' },
110 | '100%': { opacity: '1', transform: 'scale(1)' }
111 | },
112 | 'zoom-out': {
113 | '0%': { opacity: '1', transform: 'scale(1)' },
114 | '100%': { opacity: '0', transform: 'scale(.5)' }
115 | },
116 | 'rotate-90': {
117 | '0%': { transform: 'rotate(0deg)' },
118 | '100%': { transform: 'rotate(90deg)' }
119 | },
120 | 'rotate-180': {
121 | '0%': { transform: 'rotate(0deg)' },
122 | '100%': { transform: 'rotate(180deg)' }
123 | },
124 | 'rotate-360': {
125 | '0%': { transform: 'rotate(0deg)' },
126 | '100%': { transform: 'rotate(360deg)' }
127 | },
128 | 'flip-horizontal': {
129 | '0%': { transform: 'rotateY(0deg)' },
130 | '100%': { transform: 'rotateY(180deg)' }
131 | },
132 | 'flip-vertical': {
133 | '0%': { transform: 'rotateX(0deg)' },
134 | '100%': { transform: 'rotateX(180deg)' }
135 | },
136 | bouncing: {
137 | '0%': { transform: 'translateY(0)' },
138 | '50%': { transform: 'translateY(-10px)' },
139 | '100%': { transform: 'translateY(0)' }
140 | },
141 | swing: {
142 | '0%': { transform: 'rotate(0deg)' },
143 | '50%': { transform: 'rotate(15deg)' },
144 | '100%': { transform: 'rotate(0deg)' }
145 | },
146 | wobble: {
147 | '0%': { transform: 'translateX(0)' },
148 | '15%': { transform: 'translateX(-20px)' },
149 | '30%': { transform: 'translateX(20%)' },
150 | '45%': { transform: 'translateX(-15%)' },
151 | '60%': { transform: 'translateX(20px)' },
152 | '75%': { transform: 'translateX(-5%)' },
153 | '100%': { transform: 'translateX(0)' }
154 | },
155 | pulse: {
156 | '0%, 100%': { opacity: '1' },
157 | '50%': { opacity: '0.5' }
158 | },
159 | pulsing: {
160 | '0%': { transform: 'scale(1)' },
161 | '50%': { transform: 'scale(1.1)' },
162 | '100%': { transform: 'scale(1)' }
163 | },
164 | shake: {
165 | '0%': { transform: 'translateX(0)' },
166 | '25%': { transform: 'translateX(-10px)' },
167 | '50%': { transform: 'translateX(10px)' },
168 | '75%': { transform: 'translateX(-10px)' },
169 | '100%': { transform: 'translateX(0)' }
170 | },
171 | tada: {
172 | '0%': { transform: 'scale(1)' },
173 | '10%': { transform: 'scale(0.9) rotate(-3deg)' },
174 | '20%': { transform: 'scale(0.9) rotate(-3deg)' },
175 | '30%': { transform: 'scale(1.1) rotate(3deg)' },
176 | '40%': { transform: 'scale(1.1) rotate(-3deg)' },
177 | '50%': { transform: 'scale(1.1) rotate(3deg)' },
178 | '60%': { transform: 'scale(1.1) rotate(-3deg)' },
179 | '70%': { transform: 'scale(1.1) rotate(3deg)' },
180 | '80%': { transform: 'scale(1.1) rotate(-3deg)' },
181 | '90%': { transform: 'scale(1.1) rotate(3deg)' },
182 | '100%': { transform: 'scale(1) rotate(0)' }
183 | },
184 | jump: {
185 | '0%': { transform: 'translateY(0)' },
186 | '50%': { transform: 'translateY(-20px)' },
187 | '100%': { transform: 'translateY(0)' }
188 | },
189 | hang: {
190 | '0%': { transform: 'translateY(-20px)' },
191 | '50%': { transform: 'translateY(0)' },
192 | '100%': { transform: 'translateY(-20px)' }
193 | },
194 | 'roll-in': {
195 | '0%': { transform: 'translateX(-20px) rotate(-120deg)' },
196 | '100%': { transform: 'translateX(0) rotate(0)' }
197 | },
198 | 'roll-out': {
199 | '0%': { transform: 'translateX(0) rotate(0)' },
200 | '100%': { transform: 'translateX(20px) rotate(120deg)' }
201 | },
202 | float: {
203 | '0%': { transform: 'translateY(0)' },
204 | '50%': { transform: 'translateY(-10px)' },
205 | '100%': { transform: 'translateY(0)' }
206 | },
207 | sink: {
208 | '0%': { transform: 'translateY(-10px)' },
209 | '50%': { transform: 'translateY(0)' },
210 | '100%': { transform: 'translateY(-10px)' }
211 | },
212 | flash: {
213 | '0%': { opacity: '1' },
214 | '50%': { opacity: '0' },
215 | '100%': { opacity: '1' }
216 | },
217 | jiggle: {
218 | '0%': { transform: 'rotate(-3deg)' },
219 | '50%': { transform: 'rotate(3deg)' },
220 | '100%': { transform: 'rotate(-3deg)' }
221 | },
222 | 'rubber-band': {
223 | '0%': { transform: 'scale(1)' },
224 | '30%': { transform: 'scale(1.25)' },
225 | '40%': { transform: 'scale(0.75)' },
226 | '50%': { transform: 'scale(1.15)' },
227 | '65%': { transform: 'scale(0.95)' },
228 | '75%': { transform: 'scale(1.05)' },
229 | '100%': { transform: 'scale(1)' }
230 | },
231 | scale: {
232 | '0%': { transform: 'scale(1)' },
233 | '100%': { transform: 'scale(1.10)' }
234 | },
235 | 'slide-in-left': {
236 | '0%': { transform: 'translateX(-20px)' },
237 | '100%': { transform: 'translateX(0)' }
238 | },
239 | 'slide-in-right': {
240 | '0%': { transform: 'translateX(20px)' },
241 | '100%': { transform: 'translateX(0)' }
242 | },
243 | 'slide-out-left': {
244 | '0%': { transform: 'translateX(0)' },
245 | '100%': { transform: 'translateX(-20px)' }
246 | },
247 | 'slide-out-right': {
248 | '0%': { transform: 'translateX(0)' },
249 | '100%': { transform: 'translateX(20px)' }
250 | },
251 | 'spin-clockwise': {
252 | '0%': { transform: 'rotate(0deg)' },
253 | '100%': { transform: 'rotate(360deg)' }
254 | },
255 | 'spin-counter-clockwise': {
256 | '0%': { transform: 'rotate(0deg)' },
257 | '100%': { transform: 'rotate(-360deg)' }
258 | },
259 | 'flip-x': {
260 | '0%': { transform: 'scaleX(1)' },
261 | '50%': { transform: 'scaleX(-1)' },
262 | '100%': { transform: 'scaleX(1)' }
263 | },
264 | 'flip-y': {
265 | '0%': { transform: 'scaleY(1)' },
266 | '50%': { transform: 'scaleY(-1)' },
267 | '100%': { transform: 'scaleY(1)' }
268 | },
269 | blink: {
270 | '0%': { opacity: '0' },
271 | '100%': { opacity: '1' }
272 | },
273 | pop: {
274 | '0%': { transform: 'scale(1)' },
275 | '50%': { transform: 'scale(1.1)' },
276 | '100%': { transform: 'scale(1)' }
277 | },
278 | 'expand-horizontally': {
279 | '0%': { transform: 'scaleX(0)' },
280 | '100%': { transform: 'scaleX(1)' }
281 | },
282 | 'contract-horizontally': {
283 | '0%': { transform: 'scaleX(1)' },
284 | '100%': { transform: 'scaleX(0)' }
285 | },
286 | 'expand-vertically': {
287 | '0%': { transform: 'scaleY(0)' },
288 | '100%': { transform: 'scaleY(1)' }
289 | },
290 | 'contract-vertically': {
291 | '0%': { transform: 'scaleY(1)' },
292 | '100%': { transform: 'scaleY(0)' }
293 | },
294 | 'fade-in-up': {
295 | '0%': { opacity: '0', transform: 'translateY(20px)' },
296 | '100%': { opacity: '1', transform: 'translateY(0)' }
297 | },
298 | 'fade-in-down': {
299 | '0%': { opacity: '0', transform: 'translateY(-20px)' },
300 | '100%': { opacity: '1', transform: 'translateY(0)' }
301 | },
302 | 'fade-in-left': {
303 | '0%': { opacity: '0', transform: 'translateX(20px)' },
304 | '100%': { opacity: '1', transform: 'translateX(0)' }
305 | },
306 | 'fade-in-right': {
307 | '0%': { opacity: '0', transform: 'translateX(-20px)' },
308 | '100%': { opacity: '1', transform: 'translateX(0)' }
309 | },
310 | 'fade-out-up': {
311 | '0%': { opacity: '1', transform: 'translateY(0)' },
312 | '100%': { opacity: '0', transform: 'translateY(-20px)' }
313 | },
314 | 'fade-out-down': {
315 | '0%': { opacity: '1', transform: 'translateY(0)' },
316 | '100%': { opacity: '0', transform: 'translateY(20px)' }
317 | },
318 | 'fade-out-left': {
319 | '0%': { opacity: '1', transform: 'translateX(0)' },
320 | '100%': { opacity: '0', transform: 'translateX(-20px)' }
321 | },
322 | 'fade-out-right': {
323 | '0%': { opacity: '1', transform: 'translateX(0)' },
324 | '100%': { opacity: '0', transform: 'translateX(20px)' }
325 | },
326 | sway: {
327 | '0%': { transform: 'rotate(0deg)' },
328 | '50%': { transform: 'rotate(15deg)' },
329 | '100%': { transform: 'rotate(0deg)' }
330 | },
331 | 'flip-in-x': {
332 | '0%': { opacity: '0', transform: 'rotateY(90deg)' },
333 | '100%': { opacity: '1', transform: 'rotateY(0deg)' }
334 | },
335 | 'flip-in-y': {
336 | '0%': { opacity: '0', transform: 'rotateX(90deg)' },
337 | '100%': { opacity: '1', transform: 'rotateX(0deg)' }
338 | },
339 | 'flip-out-x': {
340 | '0%': { opacity: '1', transform: 'rotateY(0deg)' },
341 | '100%': { opacity: '0', transform: 'rotateY(90deg)' }
342 | },
343 | 'flip-out-y': {
344 | '0%': { opacity: '1', transform: 'rotateX(0deg)' },
345 | '100%': { opacity: '0', transform: 'rotateX(90deg)' }
346 | },
347 | 'rotate-in': {
348 | '0%': { opacity: '0', transform: 'rotate(-90deg)' },
349 | '100%': { opacity: '1', transform: 'rotate(0deg)' }
350 | },
351 | 'rotate-out': {
352 | '0%': { opacity: '1', transform: 'rotate(0deg)' },
353 | '100%': { opacity: '0', transform: 'rotate(90deg)' }
354 | },
355 | 'slide-rotate-in': {
356 | '0%': { opacity: '0', transform: 'translateX(-20px) rotate(-90deg)' },
357 | '100%': { opacity: '1', transform: 'translateX(0) rotate(0deg)' }
358 | },
359 | 'slide-rotate-out': {
360 | '0%': { opacity: '1', transform: 'translateX(0) rotate(0deg)' },
361 | '100%': { opacity: '0', transform: 'translateX(20px) rotate(90deg)' }
362 | },
363 | heartbeat: {
364 | '0%': { transform: 'scale(1)' },
365 | '25%': { transform: 'scale(1.1)' },
366 | '50%': { transform: 'scale(1)' },
367 | '75%': { transform: 'scale(0.9)' },
368 | '100%': { transform: 'scale(1)' }
369 | },
370 | 'blurred-fade-in': {
371 | '0%': { filter: 'blur(5px)', opacity: '0' },
372 | '100%': { filter: 'blur(0)', opacity: '1' }
373 | },
374 | 'horizontal-vibration': {
375 | '0%': { transform: 'translateX(0)' },
376 | '25%': { transform: 'translateX(5px)' },
377 | '50%': { transform: 'translateX(-5px)' },
378 | '75%': { transform: 'translateX(5px)' },
379 | '100%': { transform: 'translateX(0)' }
380 | },
381 | 'rotational-wave': {
382 | '0%': { transform: 'rotate(0deg)' },
383 | '25%': { transform: 'rotate(10deg)' },
384 | '50%': { transform: 'rotate(-10deg)' },
385 | '75%': { transform: 'rotate(10deg)' },
386 | '100%': { transform: 'rotate(0deg)' }
387 | },
388 | skew: {
389 | '0%': { transform: 'skew(0deg)' },
390 | '100%': { transform: 'skew(20deg)' }
391 | },
392 | 'skew-right': {
393 | '0%': { transform: 'skew(0deg)' },
394 | '100%': { transform: 'skew(-20deg)' }
395 | },
396 | 'vertical-bounce': {
397 | '0%, 100%': { transform: 'translateY(0)' },
398 | '50%': { transform: 'translateY(-20px)' }
399 | },
400 | 'horizontal-bounce': {
401 | '0%, 100%': { transform: 'translateX(0)' },
402 | '50%': { transform: 'translateX(20px)' }
403 | },
404 | tilt: {
405 | '0%': { transform: 'rotateY(0deg)' },
406 | '50%': { transform: 'rotateY(20deg)' },
407 | '100%': { transform: 'rotateY(0deg)' }
408 | },
409 | squeeze: {
410 | '0%, 100%': { transform: 'scale(1, 1)' },
411 | '50%': { transform: 'scale(1.1, 0.9)' }
412 | },
413 | 'slide-up-fade': {
414 | '0%': { opacity: '0', transform: 'translateY(50px)' },
415 | '100%': { opacity: '1', transform: 'translateY(0)' }
416 | },
417 | 'bounce-fade-in': {
418 | '0%': { transform: 'scale(0.5)', opacity: '0' },
419 | '100%': { transform: 'scale(1)', opacity: '1' }
420 | },
421 | 'swing-drop-in': {
422 | '0%': { transform: 'rotate(-30deg) translateY(-50px)', opacity: '0' },
423 | '100%': { transform: 'rotate(0deg) translateY(0)', opacity: '1' }
424 | },
425 | 'pulse-fade-in': {
426 | '0%': { transform: 'scale(0.9)', opacity: '0' },
427 | '50%': { transform: 'scale(1.05)', opacity: '0.5' },
428 | '100%': { transform: 'scale(1)', opacity: '1' }
429 | },
430 | 'impulse-rotation-right': {
431 | '0%': { transform: 'rotate(0deg)' },
432 | '50%': { transform: 'rotate(-40deg)' },
433 | '100%': { transform: 'rotate(360deg)' }
434 | },
435 | 'impulse-rotation-left': {
436 | '0%': { transform: 'rotate(0deg)' },
437 | '50%': { transform: 'rotate(40deg)' },
438 | '100%': { transform: 'rotate(-360deg)' }
439 | },
440 | dancing: {
441 | '0%': { transform: 'skew(0deg)' },
442 | '25%': { transform: 'skew(-40deg)' },
443 | '50%': { transform: 'skew(40deg)' },
444 | '75%': { transform: 'skew(-40deg)' },
445 | '100%': { transform: 'skew(0deg)' }
446 | },
447 | jelly: {
448 | '0%': { transform: 'scale(1, 1)' },
449 | '20%': { transform: 'scale(1.25, 0.75)' },
450 | '40%': { transform: 'scale(0.75, 1.25)' },
451 | '60%': { transform: 'scale(1.15, 0.85)' },
452 | '75%': { transform: 'scale(0.95, 1.05)' },
453 | '85%': { transform: 'scale(1.05, 0.95)' },
454 | '92%': { transform: 'scale(1, 1.02)' },
455 | '100%': { transform: 'scale(1, 1)' }
456 | }
457 | },
458 | animationDelay: {
459 | none: '0ms',
460 | 0: '0ms',
461 | 100: '100ms',
462 | 150: '150ms',
463 | 200: '200ms',
464 | 250: '250ms',
465 | 300: '300ms',
466 | 400: '400ms',
467 | 500: '500ms',
468 | 700: '700ms',
469 | 800: '800ms',
470 | 900: '900ms',
471 | 1000: '1000ms'
472 | },
473 | animationDuration: {
474 | none: '0ms',
475 | slower: '500ms',
476 | slow: '400ms',
477 | normal: '300ms',
478 | fast: '200ms',
479 | faster: '100ms',
480 | 0: '0ms',
481 | 100: '100ms',
482 | 150: '150ms',
483 | 200: '200ms',
484 | 250: '250ms',
485 | 300: '300ms',
486 | 400: '400ms',
487 | 500: '500ms',
488 | 700: '700ms',
489 | 800: '800ms',
490 | 900: '900ms',
491 | 1000: '1000ms'
492 | },
493 | animationSteps: {
494 | none: '0',
495 | retro: '8',
496 | normal: '16',
497 | modern: '24'
498 | },
499 | animationIterationCount: {
500 | none: '0',
501 | once: '1',
502 | twice: '2',
503 | thrice: '3',
504 | infinite: 'infinite'
505 | },
506 | animationFillMode: {
507 | none: 'none',
508 | forwards: 'forwards',
509 | backwards: 'backwards',
510 | both: 'both'
511 | },
512 | animationCubicBezier: {
513 | 'sine-in': 'cubic-bezier(0.12,0,0.39,0)',
514 | 'sine-out': 'cubic-bezier(0.39,0.575,0.565,1)',
515 | 'sine-in-out': 'cubic-bezier(0.445,0.05,0.55,0.95)',
516 | 'quad-in': 'cubic-bezier(0.55,0.085,0.68,0.53)',
517 | 'quad-out': 'cubic-bezier(0.25,0.46,0.45,0.94)',
518 | 'quad-in-out': 'cubic-bezier(0.455,0.03,0.515,0.955)',
519 | 'cubic-in': 'cubic-bezier(0.55,0.055,0.675,0.19)',
520 | 'cubic-out': 'cubic-bezier(0.215,0.61,0.355,1)',
521 | 'cubic-in-out': 'cubic-bezier(0.645,0.045,0.355,1)',
522 | 'quart-in': 'cubic-bezier(0.895,0.03,0.685,0.22)',
523 | 'quart-out': 'cubic-bezier(0.165,0.84,0.44,1)',
524 | 'quart-in-out': 'cubic-bezier(0.77,0,0.175,1)',
525 | 'quint-in': 'cubic-bezier(0.755,0.05,0.855,0.06)',
526 | 'quint-out': 'cubic-bezier(0.23,1,0.32,1)',
527 | 'quint-in-out': 'cubic-bezier(0.86,0,0.07,1)',
528 | 'expo-in': 'cubic-bezier(0.95,0.05,0.795,0.035)',
529 | 'expo-out': 'cubic-bezier(0.19,1,0.22,1)',
530 | 'expo-in-out': 'cubic-bezier(1,0,0,1)',
531 | 'circ-in': 'cubic-bezier(0.6,0.04,0.98,0.335)',
532 | 'circ-out': 'cubic-bezier(0.075,0.82,0.165,1)',
533 | 'circ-in-out': 'cubic-bezier(0.785,0.135,0.15,0.86)',
534 | 'back-in': 'cubic-bezier(0.6,-0.28,0.735,0.045)',
535 | 'back-out': 'cubic-bezier(0.175,0.885,0.32,1.275)',
536 | 'back-in-out': 'cubic-bezier(0.68,-0.55,0.265,1.55)'
537 | },
538 | animationRange: {
539 | normal: 'normal',
540 | cover: 'cover',
541 | contain: 'contain',
542 | entry: 'entry',
543 | exit: 'exit',
544 | gradual: '10% 90%',
545 | moderate: '20% 80%',
546 | brisk: '30% 70%',
547 | rapid: '40% 60%'
548 | },
549 | timeline: {
550 | none: 'none',
551 | auto: 'auto',
552 | single: '--single-timeline',
553 | scroll: 'scroll()',
554 | view: 'view()'
555 | },
556 | scrollTimeline: {
557 | single: '--single-timeline'
558 | },
559 | viewTimeline: {
560 | single: '--single-timeline'
561 | },
562 | scrollTimelineAxis: {
563 | block: 'block',
564 | inline: 'inline',
565 | x: 'x',
566 | y: 'y'
567 | },
568 | viewTimelineAxis: {
569 | block: 'block',
570 | inline: 'inline',
571 | x: 'x',
572 | y: 'y'
573 | },
574 | scrollAnimation: {
575 | single: '--single-timeline'
576 | },
577 | viewAnimation: {
578 | single: '--single-timeline'
579 | }
580 | }
581 |
--------------------------------------------------------------------------------