UNPKG

tailwindcss-motion

Version:

Tailwind Motion is a Tailwind CSS Plugin made by Rombo. It’s a simple, yet powerful, animation library for Tailwind CSS.

538 lines (487 loc) 21.2 kB
import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette.js"; import type { Config, PluginAPI } from "tailwindcss/types/config.js"; type ThemeConfig = { blur: Record<string, string>; colors: Record<string, string>; grayscale: Record<string, string>; motionBackgroundColor: Record<string, string>; motionBlur: Record<string, string>; motionGrayscale: Record<string, string>; motionOpacity: Record<string, string>; motionRotate: Record<string, string>; motionScale: Record<string, string>; motionTextColor: Record<string, string>; motionTranslate: Record<string, string>; opacity: Record<string, string>; rotate: Record<string, string>; scale: Record<string, string>; }; export const allEnterAnimations = "var(--motion-scale-in-animation), var(--motion-translate-in-animation), var(--motion-rotate-in-animation), var(--motion-filter-in-animation), var(--motion-opacity-in-animation), var(--motion-background-color-in-animation), var(--motion-text-color-in-animation)"; export const allExitAnimations = "var(--motion-scale-out-animation), var(--motion-translate-out-animation), var(--motion-rotate-out-animation), var(--motion-filter-out-animation), var(--motion-opacity-out-animation), var(--motion-background-color-out-animation), var(--motion-text-color-out-animation)"; export const allLoopAnimations = "var(--motion-scale-loop-animation), var(--motion-translate-loop-animation), var(--motion-rotate-loop-animation), var(--motion-filter-loop-animation), var(--motion-opacity-loop-animation), var(--motion-background-color-loop-animation), var(--motion-text-color-loop-animation)"; export const allLoopAndEnterAnimations = `${allEnterAnimations}, ${allLoopAnimations}`; // animation strings export const scaleInAnimation = "motion-scale-in calc(var(--motion-scale-duration, var(--motion-duration)) * var(--motion-scale-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-scale-timing, var(--motion-timing)) var(--motion-scale-delay, var(--motion-delay)) both"; export const scaleOutAnimation = "motion-scale-out calc(var(--motion-scale-duration, var(--motion-duration)) * var(--motion-scale-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-scale-timing, var(--motion-timing)) var(--motion-scale-delay, var(--motion-delay)) both"; export const scaleLoopAnimation = (type: string) => `motion-scale-loop-${type} calc(var(--motion-scale-duration, var(--motion-duration)) * var(--motion-scale-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-scale-timing, var(--motion-timing)) var(--motion-scale-delay, var(--motion-delay)) both var(--motion-scale-loop-count, var(--motion-loop-count))`; export const translateInAnimation = "motion-translate-in calc(var(--motion-translate-duration, var(--motion-duration)) * var(--motion-translate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-translate-timing, var(--motion-timing)) var(--motion-translate-delay, var(--motion-delay)) both"; export const translateOutAnimation = "motion-translate-out calc(var(--motion-translate-duration, var(--motion-duration)) * var(--motion-translate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-translate-timing, var(--motion-timing)) var(--motion-translate-delay, var(--motion-delay)) both"; export const translateLoopAnimation = (type: string) => `motion-translate-loop-${type} calc(var(--motion-translate-duration, var(--motion-duration)) * var(--motion-translate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-translate-timing, var(--motion-timing)) var(--motion-translate-delay, var(--motion-delay)) both var(--motion-translate-loop-count, var(--motion-loop-count))`; export const rotateInAnimation = "motion-rotate-in calc(var(--motion-rotate-duration, var(--motion-duration)) * var(--motion-rotate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-rotate-timing, var(--motion-timing)) var(--motion-rotate-delay, var(--motion-delay)) both"; export const rotateOutAnimation = "motion-rotate-out calc(var(--motion-rotate-duration, var(--motion-duration)) * var(--motion-rotate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-rotate-timing, var(--motion-timing)) var(--motion-rotate-delay, var(--motion-delay)) both"; export const rotateLoopAnimation = (type: string) => `motion-rotate-loop-${type} calc(var(--motion-rotate-duration, var(--motion-duration)) * var(--motion-rotate-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-rotate-timing, var(--motion-timing)) var(--motion-rotate-delay, var(--motion-delay)) both var(--motion-rotate-loop-count, var(--motion-loop-count))`; export const filterInAnimation = "motion-filter-in calc(var(--motion-filter-duration, var(--motion-duration)) * var(--motion-filter-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-filter-timing, var(--motion-timing)) var(--motion-filter-delay, var(--motion-delay)) both"; export const filterOutAnimation = "motion-filter-out calc(var(--motion-filter-duration, var(--motion-duration)) * var(--motion-filter-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-filter-timing, var(--motion-timing)) var(--motion-filter-delay, var(--motion-delay)) both"; export const filterLoopAnimation = (type: string) => `motion-filter-loop-${type} calc(var(--motion-filter-duration, var(--motion-duration)) * var(--motion-filter-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-filter-timing, var(--motion-timing)) var(--motion-filter-delay, var(--motion-delay)) both var(--motion-filter-loop-count, var(--motion-loop-count))`; export const opacityInAnimation = "motion-opacity-in calc(var(--motion-opacity-duration, var(--motion-duration)) * var(--motion-opacity-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-opacity-timing, var(--motion-timing)) var(--motion-opacity-delay, var(--motion-delay)) both"; export const opacityOutAnimation = "motion-opacity-out calc(var(--motion-opacity-duration, var(--motion-duration)) * var(--motion-opacity-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-opacity-timing, var(--motion-timing)) var(--motion-opacity-delay, var(--motion-delay)) both"; export const opacityLoopAnimation = (type: string) => `motion-opacity-loop-${type} calc(var(--motion-opacity-duration, var(--motion-duration)) * var(--motion-opacity-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-opacity-timing, var(--motion-timing)) var(--motion-opacity-delay, var(--motion-delay)) both var(--motion-opacity-loop-count, var(--motion-loop-count))`; export const backgroundColorInAnimation = "motion-background-color-in calc(var(--motion-background-color-duration, var(--motion-duration)) * var(--motion-background-color-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-background-color-timing, var(--motion-timing)) var(--motion-background-color-delay, var(--motion-delay)) both"; export const backgroundColorOutAnimation = "motion-background-color-out calc(var(--motion-background-color-duration, var(--motion-duration)) * var(--motion-background-color-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-background-color-timing, var(--motion-timing)) var(--motion-background-color-delay, var(--motion-delay)) both"; export const backgroundColorLoopAnimation = (type: string) => `motion-background-color-loop-${type} calc(var(--motion-background-color-duration, var(--motion-duration)) * var(--motion-background-color-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-background-color-timing, var(--motion-timing)) var(--motion-background-color-delay, var(--motion-delay)) both var(--motion-background-color-loop-count, var(--motion-loop-count))`; export const textColorInAnimation = "motion-text-color-in calc(var(--motion-text-color-duration, var(--motion-duration)) * var(--motion-text-color-perceptual-duration-multiplier, var(--motion-perceptual-duration-multiplier))) var(--motion-text-color-timing, var(--motion-timing)) var(--motion-text-color-delay, var(--motion-delay)) both"; export const textColorOutAnimation = "motion-text-color-out calc(var(--motion-text-color-duration, --motion-duration) * var(--motion-text-color-perceptual-duration-multiplier, --motion-perceptual-duration-multiplier)) var(--motion-text-color-timing, --motion-timing) var(--motion-text-color-delay, --motion-delay) both"; export const textColorLoopAnimation = (type: string) => `motion-text-color-loop-${type} calc(var(--motion-text-color-duration, --motion-duration) * var(--motion-text-color-perceptual-duration-multiplier, --motion-perceptual-duration-multiplier)) var(--motion-text-color-timing, --motion-timing) var(--motion-text-color-delay, --motion-delay) both var(--motion-text-color-loop-count, --motion-loop-count)`; export function addBaseAnimations( matchUtilities: PluginAPI["matchUtilities"], theme: (path: keyof ThemeConfig) => Record<string, string> ) { // scale matchUtilities( { "motion-scale-in": (value) => ({ "--motion-origin-scale-x": value, "--motion-origin-scale-y": value, "--motion-scale-in-animation": scaleInAnimation, animation: allLoopAndEnterAnimations, }), "motion-scale-x-in": (value) => ({ "--motion-origin-scale-x": value, "--motion-scale-in-animation": scaleInAnimation, animation: allLoopAndEnterAnimations, }), "motion-scale-y-in": (value) => ({ "--motion-origin-scale-y": value, "--motion-scale-in-animation": scaleInAnimation, animation: allLoopAndEnterAnimations, }), "motion-scale-out": (value) => ({ "--motion-end-scale-x": value, "--motion-end-scale-y": value, "--motion-scale-out-animation": scaleOutAnimation, animation: allExitAnimations, }), "motion-scale-x-out": (value) => ({ "--motion-end-scale-x": value, "--motion-scale-out-animation": scaleOutAnimation, animation: allExitAnimations, }), "motion-scale-y-out": (value) => ({ "--motion-end-scale-y": value, "--motion-scale-out-animation": scaleOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionScale"), } ); // scale loop matchUtilities( { "motion-scale-x-loop": (value, { modifier }) => ({ "--motion-loop-scale-x": value, "--motion-scale-loop-animation": scaleLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), "motion-scale-y-loop": (value, { modifier }) => ({ "--motion-loop-scale-y": value, "--motion-scale-loop-animation": scaleLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), "motion-scale-loop": (value, { modifier }) => ({ "--motion-loop-scale-x": value, "--motion-loop-scale-y": value, "--motion-scale-loop-animation": scaleLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionScale"), modifiers: { mirror: "mirror", reset: "reset", }, } ); // translate matchUtilities( { "motion-translate-x-in": (value) => ({ "--motion-origin-translate-x": value, "--motion-translate-in-animation": translateInAnimation, animation: allLoopAndEnterAnimations, }), "motion-translate-y-in": (value) => ({ "--motion-origin-translate-y": value, "--motion-translate-in-animation": translateInAnimation, animation: allLoopAndEnterAnimations, }), "motion-translate-x-out": (value) => ({ "--motion-end-translate-x": value, "--motion-translate-out-animation": translateOutAnimation, animation: allExitAnimations, }), "motion-translate-y-out": (value) => ({ "--motion-end-translate-y": value, "--motion-translate-out-animation": translateOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionTranslate"), supportsNegativeValues: true, } ); // translate loop matchUtilities( { "motion-translate-x-loop": (value, { modifier }) => { return { "--motion-loop-translate-x": value, "--motion-translate-loop-animation": translateLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }; }, "motion-translate-y-loop": (value, { modifier }) => { return { "--motion-loop-translate-y": value, "--motion-translate-loop-animation": translateLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }; }, }, { values: theme("motionTranslate"), supportsNegativeValues: true, modifiers: { mirror: "mirror", reset: "reset", }, } ); // rotate matchUtilities( { "motion-rotate-in": (value) => ({ "--motion-origin-rotate": value, "--motion-rotate-in-animation": rotateInAnimation, animation: allLoopAndEnterAnimations, }), "motion-rotate-out": (value) => ({ "--motion-end-rotate": value, "--motion-rotate-out-animation": rotateOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionRotate"), supportsNegativeValues: true, } ); // rotate loop matchUtilities( { "motion-rotate-loop": (value, { modifier }) => ({ "--motion-loop-rotate": value, "--motion-rotate-loop-animation": rotateLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionRotate"), supportsNegativeValues: true, modifiers: { mirror: "mirror", reset: "reset", }, } ); // blur matchUtilities( { "motion-blur-in": (value) => ({ "--motion-origin-blur": value, "--motion-filter-in-animation": filterInAnimation, animation: allLoopAndEnterAnimations, }), "motion-blur-out": (value) => ({ "--motion-end-blur": value, "--motion-filter-out-animation": filterOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionBlur"), } ); // blur loop matchUtilities( { "motion-blur-loop": (value, { modifier }) => ({ "--motion-loop-blur": value, "--motion-filter-loop-animation": filterLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionBlur"), modifiers: { mirror: "mirror", reset: "reset", }, } ); // grayscale matchUtilities( { "motion-grayscale-in": (value) => ({ "--motion-origin-grayscale": value, "--motion-filter-in-animation": filterInAnimation, animation: allLoopAndEnterAnimations, }), "motion-grayscale-out": (value) => ({ "--motion-end-grayscale": value, "--motion-filter-out-animation": filterOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionGrayscale"), } ); // grayscale loop matchUtilities( { "motion-grayscale-loop": (value, { modifier }) => ({ "--motion-loop-grayscale": value, "--motion-filter-loop-animation": filterLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionGrayscale"), modifiers: { mirror: "mirror", reset: "reset", }, } ); // opacity matchUtilities( { "motion-opacity-in": (value) => ({ "--motion-origin-opacity": value, "--motion-opacity-in-animation": opacityInAnimation, animation: allLoopAndEnterAnimations, }), "motion-opacity-out": (value) => ({ "--motion-end-opacity": value, "--motion-opacity-out-animation": opacityOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionOpacity"), } ); // opacity loop matchUtilities( { "motion-opacity-loop": (value, { modifier }) => ({ // we need to subtract 1 because of animation composition "--motion-loop-opacity": `calc(${value} - 1)`, "--motion-opacity-loop-animation": opacityLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionOpacity"), modifiers: { mirror: "mirror", reset: "reset", }, } ); // background-color matchUtilities( { "motion-bg-in": (value) => ({ "--motion-origin-background-color": value, "--motion-background-color-in-animation": backgroundColorInAnimation, animation: allLoopAndEnterAnimations, }), "motion-bg-out": (value) => ({ "--motion-end-background-color": value, "--motion-background-color-out-animation": backgroundColorOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionBackgroundColor"), type: "color", } ); // background-color loop matchUtilities( { "motion-bg-loop": (value, { modifier }) => ({ "--motion-loop-background-color": value, "--motion-background-color-loop-animation": backgroundColorLoopAnimation(modifier || "mirror"), // no animation composition because it makes colors add animation: allLoopAndEnterAnimations, }), }, { values: theme("motionBackgroundColor"), type: "color", modifiers: { mirror: "mirror", reset: "reset", }, } ); // text-color matchUtilities( { "motion-text-in": (value) => ({ "--motion-origin-text-color": value, "--motion-text-color-in-animation": textColorInAnimation, animation: allLoopAndEnterAnimations, }), "motion-text-out": (value) => ({ "--motion-end-text-color": value, "--motion-text-color-out-animation": textColorOutAnimation, animation: allExitAnimations, }), }, { values: theme("motionTextColor"), type: "color", } ); // text-color loop matchUtilities( { "motion-text-loop": (value, { modifier }) => ({ "--motion-loop-text-color": value, "--motion-text-color-loop-animation": textColorLoopAnimation( modifier || "mirror" ), animationComposition: "accumulate", animation: allLoopAndEnterAnimations, }), }, { values: theme("motionTextColor"), type: "color", modifiers: { mirror: "mirror", reset: "reset", }, } ); } export const baseAnimationsTheme: Config["theme"] = { motionScale: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => ({ ...theme("scale"), DEFAULT: "50%", }), motionTranslate: { "0": "0%", "25": "25%", "50": "50%", "75": "75%", "100": "100%", "150": "150%", DEFAULT: "25%", }, motionRotate: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => ({ ...theme("rotate"), DEFAULT: "12deg", }), motionBlur: (theme: (path: keyof ThemeConfig) => Record<string, string>) => theme("blur"), motionGrayscale: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => theme("grayscale"), motionOpacity: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => ({ ...theme("opacity"), DEFAULT: "0", "0": "0.001", }), motionBackgroundColor: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => flattenColorPalette(theme("colors")), motionTextColor: ( theme: (path: keyof ThemeConfig) => Record<string, string> ) => flattenColorPalette(theme("colors")), } as const;