├── 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 | 23 | 37 |
38 | 39 | 56 | 57 | 113 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Awesome Tailwind Animations 4 | 5 | [![en](https://img.shields.io/badge/lang-en-red.svg)](./README.md) 6 | [![es](https://img.shields.io/badge/lang-es-yellow.svg)](./README.es.md) 7 | 8 | ![GitHub stars](https://img.shields.io/github/stars/midudev/tailwind-animations) 9 | ![GitHub Forks](https://img.shields.io/github/forks/midudev/tailwind-animations) 10 | ![GitHub PRs](https://img.shields.io/github/issues-pr/midudev/tailwind-animations) 11 | ![GitHub issues](https://img.shields.io/github/issues/midudev/tailwind-animations) 12 | ![GitHub Contributors](https://img.shields.io/github/contributors/midudev/tailwind-animations) 13 | 14 | ![web](./lib/imgs/web.jpg) 15 | 16 | ![Tailwind 17 | CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4.1-blue?style=for-the-badge&logo=tailwind-css) 18 | ![Astro](https://img.shields.io/badge/Astro-4.3.3-orange?style=for-the-badge&logo=astro) 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 | [![en](https://img.shields.io/badge/lang-en-red.svg)](./README.md) 6 | [![es](https://img.shields.io/badge/lang-es-yellow.svg)](./README.es.md) 7 | 8 | ![GitHub stars](https://img.shields.io/github/stars/midudev/tailwind-animations) 9 | ![GitHub Forks](https://img.shields.io/github/forks/midudev/tailwind-animations) 10 | ![GitHub PRs](https://img.shields.io/github/issues-pr/midudev/tailwind-animations) 11 | ![GitHub issues](https://img.shields.io/github/issues/midudev/tailwind-animations) 12 | ![GitHub Contributors](https://img.shields.io/github/contributors/midudev/tailwind-animations) 13 | 14 | ![web](./lib/imgs/web.jpg) 15 | 16 | ![Tailwind 17 | CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4.1-blue?style=for-the-badge&logo=tailwind-css) 18 | ![Astro](https://img.shields.io/badge/Astro-4.3.3-orange?style=for-the-badge&logo=astro) 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 |
25 | 26 | v.{version} 29 | 37 | 38 | 39 |
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 | 65 |
66 | 2. Add to your configuration 69 |
72 |
 74 |             
 75 |             
 78 |           
79 |
80 |
81 |
82 | 83 |
87 | 104 | 121 | 138 | 151 |
152 | 153 |
156 | { 157 | Object.keys(animation).map((animationKey) => { 158 | return ( 159 |
163 |
164 | 165 |

{animationKey}

166 |
167 | 173 |
174 | ) 175 | }) 176 | } 177 |
178 | 179 | 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 | --------------------------------------------------------------------------------