CSS Transition Timing Functions

Control the acceleration and pacing of your CSS transitions with timing functions

What are CSS Transition Timing Functions?

CSS Transition Timing Functions determine how intermediate values of a transition are calculated, allowing you to control the acceleration and pacing of animations. They define the rate of change of a property over time, creating more natural and engaging user experiences.

Key Timing Function Types:

  • Built-in functions - ease, linear, ease-in, ease-out, ease-in-out
  • Step functions - step-start, step-end, steps()
  • Cubic Bรฉzier functions - cubic-bezier() for custom curves
  • Material Design curves - Standardized easing curves for UI animations

CSS Transition Timing Functions Reference

FunctionDescriptionValues
easeSlow start, fast middle, slow end (default)cubic-bezier(0.25, 0.1, 0.25, 1)
linearConstant speed throughoutcubic-bezier(0, 0, 1, 1)
ease-inSlow start, fast endcubic-bezier(0.42, 0, 1, 1)
ease-outFast start, slow endcubic-bezier(0, 0, 0.58, 1)
ease-in-outSlow start and end, fast middlecubic-bezier(0.42, 0, 0.58, 1)
step-startJumps to end state immediatelysteps(1, jump-start)
step-endJumps to end state at the endsteps(1, jump-end)
steps()Discrete steps instead of smooth transitionsteps(n, jump-term)
cubic-bezier()Custom timing function with control pointscubic-bezier(x1, y1, x2, y2)

CSS Transition Timing Functions in Action

Example Code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive CSS Timing Functions Showcase</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
        
        :root {
            --primary: #6366f1;
            --secondary: #ec4899;
            --accent: #06b6d4;
            --success: #10b981;
            --warning: #f59e0b;
            --danger: #ef4444;
            --dark: #0f172a;
            --light: #f8fafc;
            --glass: rgba(255, 255, 255, 0.1);
            --glass-border: rgba(255, 255, 255, 0.2);
            --shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
            --shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            color: white;
            overflow-x: hidden;
        }

        .hero {
            text-align: center;
            padding: 4rem 2rem;
            background: linear-gradient(135deg, rgba(99, 102, 241, 0.3), rgba(236, 72, 153, 0.3));
            backdrop-filter: blur(10px);
            margin-bottom: 3rem;
        }

        .hero h1 {
            font-size: clamp(2.5rem, 5vw, 4rem);
            font-weight: 700;
            background: linear-gradient(45deg, #fff, #e2e8f0);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            margin-bottom: 1rem;
            text-shadow: 0 0 30px rgba(255, 255, 255, 0.3);
        }

        .hero p {
            font-size: 1.25rem;
            opacity: 0.9;
            max-width: 600px;
            margin: 0 auto 2rem;
        }

        .timing-selector {
            display: flex;
            justify-content: center;
            gap: 0.5rem;
            margin-bottom: 2rem;
            flex-wrap: wrap;
            padding: 1rem;
            background: var(--glass);
            border-radius: 20px;
            backdrop-filter: blur(10px);
            max-width: 900px;
            margin: 0 auto 2rem;
        }

        .timing-btn {
            padding: 0.75rem 1.25rem;
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 25px;
            color: white;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(5px);
            font-weight: 500;
            font-size: 0.85rem;
            position: relative;
            overflow: hidden;
        }

        .timing-btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.6s;
        }

        .timing-btn:hover::before {
            left: 100%;
        }

        .timing-btn:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
        }

        .timing-btn.active {
            background: var(--primary);
            border-color: var(--primary);
            transform: translateY(-2px);
            box-shadow: 0 0 20px rgba(99, 102, 241, 0.5);
        }

        .control-panel {
            display: flex;
            justify-content: center;
            gap: 1rem;
            margin-bottom: 2rem;
            flex-wrap: wrap;
            padding: 0 2rem;
        }

        .control-btn {
            padding: 0.75rem 1.5rem;
            background: var(--glass);
            border: 1px solid var(--glass-border);
            border-radius: 12px;
            color: white;
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(10px);
            font-weight: 500;
        }

        .control-btn:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: translateY(-2px);
            box-shadow: var(--shadow);
        }

        .control-btn.active {
            background: var(--secondary);
            border-color: var(--secondary);
            transform: translateY(-2px);
            box-shadow: 0 0 20px rgba(236, 72, 153, 0.5);
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            padding: 0 2rem;
        }

        .section {
            margin-bottom: 4rem;
            background: var(--glass);
            border: 1px solid var(--glass-border);
            border-radius: 24px;
            padding: 2rem;
            backdrop-filter: blur(20px);
            box-shadow: var(--shadow);
        }

        .section h2 {
            font-size: 2rem;
            font-weight: 600;
            margin-bottom: 1rem;
            background: linear-gradient(45deg, #fff, #e2e8f0);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .section p {
            opacity: 0.8;
            margin-bottom: 2rem;
            font-size: 1.1rem;
        }

        /* Enhanced Racing Demo */
        .race-container {
            display: grid;
            gap: 1.5rem;
            margin: 2rem 0;
        }

        .race-track {
            background: rgba(0, 0, 0, 0.2);
            border-radius: 50px;
            height: 80px;
            position: relative;
            padding: 10px;
            overflow: hidden;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .race-track:hover {
            background: rgba(0, 0, 0, 0.3);
            transform: scale(1.02);
        }

        .race-track::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 20px;
            right: 20px;
            height: 2px;
            background: repeating-linear-gradient(90deg, #fff 0, #fff 10px, transparent 10px, transparent 20px);
            opacity: 0.3;
            transform: translateY(-50%);
        }

        .racer {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            position: absolute;
            top: 10px;
            left: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            color: white;
            transition: all 3s;
            box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
            cursor: pointer;
        }

        .race-info {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 0.5rem;
            opacity: 0.8;
        }

        .race-controls {
            display: flex;
            gap: 0.5rem;
            align-items: center;
        }

        .race-btn {
            padding: 0.25rem 0.75rem;
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 15px;
            color: white;
            font-size: 0.8rem;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .race-btn:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: scale(1.05);
        }

        .racing .racer {
            left: calc(100% - 70px);
        }

        .single-racing .racer {
            left: calc(100% - 70px);
        }

        /* Interactive Playground */
        .playground {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 2rem;
            margin: 2rem 0;
        }

        .playground-item {
            background: rgba(255, 255, 255, 0.05);
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 16px;
            padding: 2rem;
            text-align: center;
        }

        .playground-title {
            font-size: 1.2rem;
            font-weight: 600;
            margin-bottom: 1rem;
            color: var(--accent);
        }

        /* Bouncing Balls */
        .ball-container {
            height: 200px;
            position: relative;
            background: rgba(0, 0, 0, 0.1);
            border-radius: 12px;
            overflow: hidden;
            margin: 1rem 0;
        }

        .ball {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            position: absolute;
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);
            background: linear-gradient(45deg, var(--primary), var(--secondary));
            cursor: pointer;
            transition: transform 1s;
        }

        .bouncing .ball {
            transform: translateX(-50%) translateY(-150px);
        }

        /* Rotating Elements */
        .rotation-demo {
            display: flex;
            justify-content: space-around;
            margin: 2rem 0;
            flex-wrap: wrap;
            gap: 1rem;
        }

        .rotate-element {
            width: 80px;
            height: 80px;
            background: linear-gradient(45deg, var(--success), var(--accent));
            border-radius: 16px;
            cursor: pointer;
            transition: transform 2s;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
        }

        .rotating .rotate-element {
            transform: rotate(360deg) scale(1.2);
        }

        /* Scaling Elements */
        .scale-demo {
            display: flex;
            justify-content: space-around;
            margin: 2rem 0;
            flex-wrap: wrap;
            gap: 1rem;
        }

        .scale-element {
            width: 60px;
            height: 60px;
            background: linear-gradient(45deg, var(--warning), var(--danger));
            border-radius: 12px;
            cursor: pointer;
            transition: transform 1.5s;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.2rem;
        }

        .scaling .scale-element {
            transform: scale(1.8);
        }

        /* Slider Controls */
        .slider-container {
            margin: 2rem 0;
        }

        .slider-group {
            display: flex;
            align-items: center;
            gap: 1rem;
            margin: 1rem 0;
            flex-wrap: wrap;
        }

        .slider {
            flex: 1;
            min-width: 200px;
            height: 8px;
            border-radius: 4px;
            background: rgba(255, 255, 255, 0.2);
            outline: none;
            cursor: pointer;
        }

        .slider::-webkit-slider-thumb {
            appearance: none;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: var(--primary);
            cursor: pointer;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
        }

        .slider::-moz-range-thumb {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: var(--primary);
            cursor: pointer;
            border: none;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
        }

        /* Interactive Cards Enhanced */
        .cards-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 2rem;
            margin: 2rem 0;
        }

        .interactive-card {
            background: var(--glass);
            border: 1px solid var(--glass-border);
            border-radius: 16px;
            padding: 2rem;
            text-align: center;
            cursor: pointer;
            transition: all 0.6s;
            backdrop-filter: blur(10px);
            position: relative;
            overflow: hidden;
        }

        .interactive-card::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
            transition: left 0.6s;
        }

        .interactive-card:hover::before {
            left: 100%;
        }

        .interactive-card:hover {
            transform: translateY(-10px) scale(1.05);
            box-shadow: var(--shadow-lg);
        }

        .card-icon {
            font-size: 3rem;
            margin-bottom: 1rem;
            opacity: 0.8;
            transition: all 0.3s ease;
        }

        .interactive-card:hover .card-icon {
            transform: rotate(10deg) scale(1.1);
        }

        .card-title {
            font-size: 1.25rem;
            font-weight: 600;
            margin-bottom: 0.5rem;
        }

        .card-description {
            opacity: 0.7;
            font-size: 0.9rem;
        }

        .card-demo-btn {
            margin-top: 1rem;
            padding: 0.5rem 1rem;
            background: rgba(255, 255, 255, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 20px;
            color: white;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 0.85rem;
        }

        .card-demo-btn:hover {
            background: rgba(255, 255, 255, 0.2);
            transform: scale(1.05);
        }

        /* Enhanced Button Playground */
        .button-playground {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1.5rem;
            margin: 2rem 0;
        }

        .demo-button {
            padding: 1rem 2rem;
            border: none;
            border-radius: 12px;
            color: white;
            font-weight: 600;
            cursor: pointer;
            position: relative;
            overflow: hidden;
            transition: all 0.4s;
            background: linear-gradient(45deg, var(--primary), var(--secondary));
        }

        .demo-button::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(45deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.1));
            transition: left 0.5s;
        }

        .demo-button:hover::before {
            left: 100%;
        }

        .demo-button:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 30px rgba(99, 102, 241, 0.4);
        }

        .demo-button:active {
            transform: translateY(-2px) scale(0.98);
        }

        /* Custom Bezier Playground */
        .bezier-playground {
            background: rgba(0, 0, 0, 0.2);
            border-radius: 16px;
            padding: 2rem;
            margin: 2rem 0;
        }

        .bezier-controls {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 1rem;
            margin-bottom: 2rem;
        }

        .bezier-input {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
        }

        .bezier-input label {
            font-size: 0.9rem;
            opacity: 0.8;
        }

        .bezier-input input {
            padding: 0.5rem;
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 8px;
            background: rgba(255, 255, 255, 0.1);
            color: white;
            outline: none;
        }

        .bezier-input input:focus {
            border-color: var(--primary);
            box-shadow: 0 0 10px rgba(99, 102, 241, 0.3);
        }

        .bezier-demo {
            width: 100px;
            height: 100px;
            background: linear-gradient(45deg, var(--accent), var(--success));
            border-radius: 12px;
            margin: 2rem auto;
            cursor: pointer;
            transition: transform 2s;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 2rem;
        }

        .bezier-active .bezier-demo {
            transform: translateX(200px) rotate(180deg) scale(1.5);
        }

        /* Physics Simulations */
        .physics-demo {
            height: 300px;
            background: rgba(0, 0, 0, 0.1);
            border-radius: 16px;
            position: relative;
            overflow: hidden;
            margin: 2rem 0;
            cursor: pointer;
        }

        .physics-object {
            width: 50px;
            height: 50px;
            background: linear-gradient(45deg, var(--primary), var(--secondary));
            border-radius: 50%;
            position: absolute;
            top: 20px;
            left: 20px;
            transition: all 2s;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
        }

        .physics-active .physics-object {
            left: calc(100% - 70px);
            top: calc(100% - 70px);
        }

        /* Timing function classes */
        .timing-ease { transition-timing-function: ease; }
        .timing-linear { transition-timing-function: linear; }
        .timing-ease-in { transition-timing-function: ease-in; }
        .timing-ease-out { transition-timing-function: ease-out; }
        .timing-ease-in-out { transition-timing-function: ease-in-out; }
        .timing-bounce { transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55); }
        .timing-elastic { transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }
        .timing-back { transition-timing-function: cubic-bezier(0.68, -0.6, 0.32, 1.6); }
        .timing-anticipate { transition-timing-function: cubic-bezier(0.36, 0, 0.66, -0.56); }
        .timing-overshoot { transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }

        /* Morphing Shapes Enhanced */
        .morphing-container {
            display: flex;
            justify-content: center;
            gap: 3rem;
            margin: 3rem 0;
            flex-wrap: wrap;
        }

        .morph-shape {
            width: 100px;
            height: 100px;
            background: linear-gradient(45deg, var(--accent), var(--success));
            cursor: pointer;
            transition: all 1s;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
        }

        .morph-circle { border-radius: 50%; }
        .morph-square { border-radius: 0; }
        .morph-diamond { transform: rotate(45deg); border-radius: 20px; }

        .morphing .morph-shape:nth-child(1) {
            border-radius: 0;
            transform: rotate(0deg) scale(1.2);
            background: linear-gradient(45deg, var(--danger), var(--warning));
        }

        .morphing .morph-shape:nth-child(2) {
            border-radius: 20px;
            transform: rotate(45deg) scale(0.8);
            background: linear-gradient(45deg, var(--success), var(--accent));
        }

        .morphing .morph-shape:nth-child(3) {
            border-radius: 50%;
            transform: rotate(0deg) scale(1.1);
            background: linear-gradient(45deg, var(--primary), var(--secondary));
        }

        /* Progress Bars Enhanced */
        .progress-container {
            margin: 2rem 0;
        }

        .progress-item {
            margin: 1.5rem 0;
            cursor: pointer;
        }

        .progress-item:hover .progress-bar {
            transform: scale(1.02);
        }

        .progress-label {
            display: flex;
            justify-content: space-between;
            margin-bottom: 0.5rem;
            font-size: 0.9rem;
            opacity: 0.8;
        }

        .progress-bar {
            width: 100%;
            height: 12px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 6px;
            overflow: hidden;
            position: relative;
            transition: transform 0.3s ease;
        }

        .progress-fill {
            height: 100%;
            width: 0%;
            background: linear-gradient(90deg, var(--primary), var(--secondary));
            border-radius: 6px;
            transition: width 2s;
            position: relative;
        }

        .progress-fill::after {
            content: '';
            position: absolute;
            top: 0;
            right: 0;
            width: 20px;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3));
        }

        .progressing .progress-fill {
            width: 85%;
        }

        .progress-individual.progressing .progress-fill {
            width: 85%;
        }

        /* Responsive */
        @media (max-width: 768px) {
            .hero { padding: 2rem 1rem; }
            .container { padding: 0 1rem; }
            .section { padding: 1.5rem; }
            .timing-selector { flex-direction: column; gap: 0.5rem; }
            .morphing-container, .floating-demo { gap: 1rem; }
            .playground { grid-template-columns: 1fr; }
            .bezier-controls { grid-template-columns: 1fr; }
        }

        /* Animation for initial load */
        @keyframes fadeInUp {
            from { opacity: 0; transform: translateY(30px); }
            to { opacity: 1; transform: translateY(0); }
        }

        .section {
            animation: fadeInUp 0.8s ease-out forwards;
        }

        .section:nth-child(2) { animation-delay: 0.1s; }
        .section:nth-child(3) { animation-delay: 0.2s; }
        .section:nth-child(4) { animation-delay: 0.3s; }
        .section:nth-child(5) { animation-delay: 0.4s; }
        .section:nth-child(6) { animation-delay: 0.5s; }
    </style>
</head>
<body>
    <div class="hero">
        <h1>CSS Timing Functions</h1>
        <p>Interactive showcase of animation timing functions with advanced controls and demonstrations</p>
        
        <div class="timing-selector">
            <button class="timing-btn active" data-timing="ease">Ease</button>
            <button class="timing-btn" data-timing="linear">Linear</button>
            <button class="timing-btn" data-timing="ease-in">Ease In</button>
            <button class="timing-btn" data-timing="ease-out">Ease Out</button>
            <button class="timing-btn" data-timing="ease-in-out">Ease In Out</button>
            <button class="timing-btn" data-timing="bounce">Bounce</button>
            <button class="timing-btn" data-timing="elastic">Elastic</button>
            <button class="timing-btn" data-timing="back">Back</button>
            <button class="timing-btn" data-timing="anticipate">Anticipate</button>
            <button class="timing-btn" data-timing="overshoot">Overshoot</button>
        </div>

        <div class="control-panel">
            <button class="control-btn active" data-action="start-race">๐Ÿ Start Race</button>
            <button class="control-btn" data-action="morph-shapes">๐Ÿ”„ Morph Shapes</button>
            <button class="control-btn" data-action="bounce-balls">โšฝ Bounce Balls</button>
            <button class="control-btn" data-action="rotate-elements">๐Ÿ”„ Rotate Elements</button>
            <button class="control-btn" data-action="scale-elements">๐Ÿ“ Scale Elements</button>
            <button class="control-btn" data-action="fill-progress">๐Ÿ“Š Fill Progress</button>
            <button class="control-btn" data-action="physics-sim">๐Ÿงช Physics Sim</button>
            <button class="control-btn" data-action="reset-all">๐Ÿ”„ Reset All</button>
        </div>
    </div>

    <div class="container">
        <section class="section">
            <h2>๐Ÿƒ Interactive Racing Demonstration</h2>
            <p>Watch different timing functions compete in a race. Click individual tracks to test specific timing functions, or use the timing selector above to apply globally.</p>
            
            <div class="race-container">
                <div class="race-track" data-timing="ease">
                    <div class="racer ease timing-ease">๐Ÿš—</div>
                    <div class="race-info">
                        <span>Ease (Default)</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="ease">Test</button>
                            <span>cubic-bezier(0.25, 0.1, 0.25, 1)</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="linear">
                    <div class="racer linear timing-linear">๐ŸŽ๏ธ</div>
                    <div class="race-info">
                        <span>Linear</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="linear">Test</button>
                            <span>Constant speed</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="ease-in">
                    <div class="racer ease-in timing-ease-in">๐Ÿš™</div>
                    <div class="race-info">
                        <span>Ease In</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="ease-in">Test</button>
                            <span>Slow start, fast finish</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="ease-out">
                    <div class="racer ease-out timing-ease-out">๐Ÿš•</div>
                    <div class="race-info">
                        <span>Ease Out</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="ease-out">Test</button>
                            <span>Fast start, slow finish</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="ease-in-out">
                    <div class="racer ease-in-out timing-ease-in-out">๐Ÿ</div>
                    <div class="race-info">
                        <span>Ease In Out</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="ease-in-out">Test</button>
                            <span>Slow start and finish</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="bounce">
                    <div class="racer bounce timing-bounce">โšก</div>
                    <div class="race-info">
                        <span>Bounce</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="bounce">Test</button>
                            <span>cubic-bezier(0.68, -0.55, 0.27, 1.55)</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="elastic">
                    <div class="racer elastic timing-elastic">๐ŸŽพ</div>
                    <div class="race-info">
                        <span>Elastic</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="elastic">Test</button>
                            <span>cubic-bezier(0.34, 1.56, 0.64, 1)</span>
                        </div>
                    </div>
                </div>
                
                <div class="race-track" data-timing="back">
                    <div class="racer back timing-back">๐ŸŽช</div>
                    <div class="race-info">
                        <span>Back</span>
                        <div class="race-controls">
                            <button class="race-btn" data-action="race-single" data-target="back">Test</button>
                            <span>cubic-bezier(0.68, -0.6, 0.32, 1.6)</span>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <section class="section">
            <h2>๐ŸŽฎ Interactive Playground</h2>
            <p>Multiple interactive demonstrations you can trigger individually or apply timing functions globally using the selector above.</p>
            
            <div class="playground">
                <div class="playground-item">
                    <div class="playground-title">โšฝ Bouncing Balls</div>
                    <div class="ball-container">
                        <div class="ball timing-ease" data-demo="ball">๐Ÿ€</div>
                    </div>
                    <button class="card-demo-btn" data-action="bounce-single">Bounce Ball</button>
                </div>
                
                <div class="playground-item">
                    <div class="playground-title">๐Ÿ”„ Rotation Demo</div>
                    <div class="rotation-demo">
                        <div class="rotate-element timing-ease" data-demo="rotate">๐ŸŽฏ</div>
                    </div>
                    <button class="card-demo-btn" data-action="rotate-single">Rotate Element</button>
                </div>
                
                <div class="playground-item">
                    <div class="playground-title">๐Ÿ“ Scaling Demo</div>
                    <div class="scale-demo">
                        <div class="scale-element timing-ease" data-demo="scale">๐Ÿ’Ž</div>
                    </div>
                    <button class="card-demo-btn" data-action="scale-single">Scale Element</button>
                </div>
                
                <div class="playground-item">
                    <div class="playground-title">๐Ÿงช Physics Simulation</div>
                    <div class="physics-demo">
                        <div class="physics-object timing-ease">โšก</div>
                    </div>
                    <button class="card-demo-btn" data-action="physics-single">Start Physics</button>
                </div>
            </div>
        </section>

        <section class="section">
            <h2>๐ŸŽ›๏ธ Custom Bezier Playground</h2>
            <p>Create your own custom timing functions using cubic-bezier values. Adjust the parameters and test the result!</p>
            
            <div class="bezier-playground">
                <div class="bezier-controls">
                    <div class="bezier-input">
                        <label>X1 (Start Control)</label>
                        <input type="number" id="x1" min="0" max="1" step="0.01" value="0.25">
                    </div>
                    <div class="bezier-input">
                        <label>Y1 (Start Control)</label>
                        <input type="number" id="y1" min="-2" max="2" step="0.01" value="0.1">
                    </div>
                    <div class="bezier-input">
                        <label>X2 (End Control)</label>
                        <input type="number" id="x2" min="0" max="1" step="0.01" value="0.25">
                    </div>
                    <div class="bezier-input">
                        <label>Y2 (End Control)</label>
                        <input type="number" id="y2" min="-2" max="2" step="0.01" value="1">
                    </div>
                </div>
                
                <div class="bezier-demo" id="bezier-demo">๐ŸŽจ</div>
                <div style="text-align: center; opacity: 0.8;">
                    <p id="bezier-display">cubic-bezier(0.25, 0.1, 0.25, 1)</p>
                    <button class="card-demo-btn" id="test-bezier">Test Custom Bezier</button>
                </div>
            </div>
        </section>

        <section class="section">
            <h2>๐ŸŽฎ Enhanced Interactive Cards</h2>
            <p>Hover over these cards and click the demo buttons to see different timing functions in action with various effects.</p>
            
            <div class="cards-grid">
                <div class="interactive-card timing-ease" data-timing="ease">
                    <div class="card-icon">โšก</div>
                    <div class="card-title">Ease</div>
                    <div class="card-description">Natural acceleration and deceleration</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="ease">Demo Effect</button>
                </div>
                
                <div class="interactive-card timing-bounce" data-timing="bounce">
                    <div class="card-icon">๐ŸŽพ</div>
                    <div class="card-title">Bounce</div>
                    <div class="card-description">Playful bouncing effect</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="bounce">Demo Effect</button>
                </div>
                
                <div class="interactive-card timing-elastic" data-timing="elastic">
                    <div class="card-icon">๐ŸŽช</div>
                    <div class="card-title">Elastic</div>
                    <div class="card-description">Springy elastic motion</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="elastic">Demo Effect</button>
                </div>
                
                <div class="interactive-card timing-back" data-timing="back">
                    <div class="card-icon">๐Ÿ”„</div>
                    <div class="card-title">Back</div>
                    <div class="card-description">Pull back before moving forward</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="back">Demo Effect</button>
                </div>
                
                <div class="interactive-card timing-anticipate" data-timing="anticipate">
                    <div class="card-icon">๐ŸŽฏ</div>
                    <div class="card-title">Anticipate</div>
                    <div class="card-description">Anticipation before action</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="anticipate">Demo Effect</button>
                </div>
                
                <div class="interactive-card timing-ease-in-out" data-timing="ease-in-out">
                    <div class="card-icon">๐ŸŒŠ</div>
                    <div class="card-title">Ease In Out</div>
                    <div class="card-description">Smooth wave-like motion</div>
                    <button class="card-demo-btn" data-action="card-demo" data-target="ease-in-out">Demo Effect</button>
                </div>
            </div>
        </section>

        <section class="section">
            <h2>๐ŸŽฏ Enhanced Button Playground</h2>
            <p>Interactive buttons with different timing functions. Click and hover to experience the differences in animation feel.</p>
            
            <div class="button-playground">
                <button class="demo-button timing-ease" data-timing="ease">Ease Button</button>
                <button class="demo-button timing-linear" data-timing="linear">Linear Button</button>
                <button class="demo-button timing-bounce" data-timing="bounce">Bounce Button</button>
                <button class="demo-button timing-elastic" data-timing="elastic">Elastic Button</button>
                <button class="demo-button timing-back" data-timing="back">Back Button</button>
                <button class="demo-button timing-anticipate" data-timing="anticipate">Anticipate Button</button>
                <button class="demo-button timing-ease-in" data-timing="ease-in">Ease In Button</button>
                <button class="demo-button timing-ease-out" data-timing="ease-out">Ease Out Button</button>
            </div>
        </section>

        <section class="section">
            <h2>๐Ÿ”„ Morphing Shapes</h2>
            <p>Watch these shapes transform using different timing functions. Click individual shapes or use the global controls.</p>
            
            <div class="morphing-container">
                <div class="morph-shape morph-circle timing-bounce" data-demo="morph">1</div>
                <div class="morph-shape morph-square timing-elastic" data-demo="morph">2</div>
                <div class="morph-shape morph-diamond timing-ease-in-out" data-demo="morph">3</div>
            </div>
            
            <div style="text-align: center;">
                <button class="card-demo-btn" data-action="morph-single">Morph Shapes</button>
            </div>
        </section>

        <section class="section">
            <h2>๐Ÿ“Š Interactive Progress Bars</h2>
            <p>Click individual progress bars to see different timing functions in action, or use the global controls.</p>
            
            <div class="progress-container">
                <div class="progress-item" data-timing="ease">
                    <div class="progress-label">
                        <span>Ease</span>
                        <span>85%</span>
                    </div>
                    <div class="progress-bar">
                        <div class="progress-fill timing-ease"></div>
                    </div>
                </div>
                
                <div class="progress-item" data-timing="linear">
                    <div class="progress-label">
                        <span>Linear</span>
                        <span>85%</span>
                    </div>
                    <div class="progress-bar">
                        <div class="progress-fill timing-linear"></div>
                    </div>
                </div>
                
                <div class="progress-item" data-timing="bounce">
                    <div class="progress-label">
                        <span>Bounce</span>
                        <span>85%</span>
                    </div>
                    <div class="progress-bar">
                        <div class="progress-fill timing-bounce"></div>
                    </div>
                </div>
                
                <div class="progress-item" data-timing="elastic">
                    <div class="progress-label">
                        <span>Elastic</span>
                        <span>85%</span>
                    </div>
                    <div class="progress-bar">
                        <div class="progress-fill timing-elastic"></div>
                    </div>
                </div>
                
                <div class="progress-item" data-timing="back">
                    <div class="progress-label">
                        <span>Back</span>
                        <span>85%</span>
                    </div>
                    <div class="progress-bar">
                        <div class="progress-fill timing-back"></div>
                    </div>
                </div>
            </div>
        </section>

        <section class="section">
            <h2>โš™๏ธ Dynamic Duration Controls</h2>
            <p>Adjust animation duration and easing dynamically to see how timing functions behave at different speeds.</p>
            
            <div class="slider-container">
                <div class="slider-group">
                    <label>Duration:</label>
                    <input type="range" id="duration-slider" class="slider" min="0.5" max="5" step="0.1" value="2">
                    <span id="duration-value">2.0s</span>
                </div>
                
                <div style="text-align: center; margin: 2rem 0;">
                    <div class="morph-shape morph-circle timing-ease" id="dynamic-demo" style="margin: 0 auto;">๐ŸŽฎ</div>
                </div>
                
                <div style="text-align: center;">
                    <button class="card-demo-btn" id="test-duration">Test Dynamic Duration</button>
                </div>
            </div>
        </section>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const timingButtons = document.querySelectorAll('.timing-btn');
            const controlButtons = document.querySelectorAll('.control-btn');
            let currentTiming = 'ease';
            
            // Global timing function selector
            timingButtons.forEach(button => {
                button.addEventListener('click', function() {
                    timingButtons.forEach(btn => btn.classList.remove('active'));
                    this.classList.add('active');
                    currentTiming = this.dataset.timing;
                    updateGlobalTiming();
                });
            });
            
            function updateGlobalTiming() {
                document.querySelectorAll('[data-demo]').forEach(element => {
                    element.className = element.className.replace(/timing-w+/g, '');
                    element.classList.add('timing-' + currentTiming);
                });
            }
            
            // Control panel buttons
            controlButtons.forEach(button => {
                button.addEventListener('click', function() {
                    controlButtons.forEach(btn => btn.classList.remove('active'));
                    this.classList.add('active');
                    
                    const action = this.dataset.action;
                    handleAction(action);
                });
            });
            
            function handleAction(action) {
                document.body.classList.remove('racing', 'morphing', 'bouncing', 'rotating', 'scaling', 'progressing', 'physics-active', 'single-racing');
                
                setTimeout(function() {
                    switch(action) {
                        case 'start-race':
                            document.body.classList.add('racing');
                            break;
                        case 'morph-shapes':
                            document.body.classList.add('morphing');
                            break;
                        case 'bounce-balls':
                            document.body.classList.add('bouncing');
                            break;
                        case 'rotate-elements':
                            document.body.classList.add('rotating');
                            break;
                        case 'scale-elements':
                            document.body.classList.add('scaling');
                            break;
                        case 'fill-progress':
                            document.body.classList.add('progressing');
                            break;
                        case 'physics-sim':
                            document.body.classList.add('physics-active');
                            break;
                        case 'reset-all':
                            break;
                    }
                }, 50);
            }
            
            // Individual race buttons
            document.querySelectorAll('.race-btn').forEach(button => {
                button.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const target = this.dataset.target;
                    const raceTrack = this.closest('.race-track');
                    
                    raceTrack.classList.remove('single-racing');
                    setTimeout(function() {
                        raceTrack.classList.add('single-racing');
                    }, 50);
                });
            });
            
            // Individual demo buttons
            document.querySelectorAll('[data-action="bounce-single"]').forEach(button => {
                button.addEventListener('click', function() {
                    const ball = this.parentElement.querySelector('.ball');
                    ball.classList.remove('bouncing');
                    setTimeout(function() { ball.classList.add('bouncing'); }, 50);
                });
            });
            
            document.querySelectorAll('[data-action="rotate-single"]').forEach(button => {
                button.addEventListener('click', function() {
                    const element = this.parentElement.querySelector('.rotate-element');
                    element.classList.remove('rotating');
                    setTimeout(function() { element.classList.add('rotating'); }, 50);
                });
            });
            
            document.querySelectorAll('[data-action="scale-single"]').forEach(button => {
                button.addEventListener('click', function() {
                    const element = this.parentElement.querySelector('.scale-element');
                    element.classList.remove('scaling');
                    setTimeout(function() { element.classList.add('scaling'); }, 50);
                });
            });
            
            document.querySelectorAll('[data-action="physics-single"]').forEach(button => {
                button.addEventListener('click', function() {
                    const demo = this.parentElement.querySelector('.physics-demo');
                    demo.classList.remove('physics-active');
                    setTimeout(function() { demo.classList.add('physics-active'); }, 50);
                });
            });
            
            document.querySelectorAll('[data-action="morph-single"]').forEach(button => {
                button.addEventListener('click', function() {
                    const container = this.parentElement.querySelector('.morphing-container') || 
                                   document.querySelector('.morphing-container');
                    container.classList.remove('morphing');
                    setTimeout(function() { container.classList.add('morphing'); }, 50);
                });
            });
            
            // Card demo buttons
            document.querySelectorAll('[data-action="card-demo"]').forEach(button => {
                button.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const card = this.closest('.interactive-card');
                    card.style.transform = 'translateY(-20px) scale(1.1) rotate(5deg)';
                    setTimeout(function() {
                        card.style.transform = '';
                    }, 600);
                });
            });
            
            // Progress bar individual clicks
            document.querySelectorAll('.progress-item').forEach(item => {
                item.addEventListener('click', function() {
                    this.classList.remove('progress-individual');
                    setTimeout(function() {
                        item.classList.add('progress-individual', 'progressing');
                    }, 50);
                });
            });
            
            // Custom bezier controls
            const bezierInputs = ['x1', 'y1', 'x2', 'y2'];
            const bezierDisplay = document.getElementById('bezier-display');
            const bezierDemo = document.getElementById('bezier-demo');
            
            function updateBezier() {
                const x1 = document.getElementById('x1').value;
                const y1 = document.getElementById('y1').value;
                const x2 = document.getElementById('x2').value;
                const y2 = document.getElementById('y2').value;
                
                const bezierValue = 'cubic-bezier(' + x1 + ', ' + y1 + ', ' + x2 + ', ' + y2 + ')';
                bezierDisplay.textContent = bezierValue;
                bezierDemo.style.transitionTimingFunction = bezierValue;
            }
            
            bezierInputs.forEach(function(id) {
                document.getElementById(id).addEventListener('input', updateBezier);
            });
            
            document.getElementById('test-bezier').addEventListener('click', function() {
                const container = document.querySelector('.bezier-playground');
                container.classList.remove('bezier-active');
                setTimeout(function() {
                    container.classList.add('bezier-active');
                }, 50);
            });
            
            // Duration slider
            const durationSlider = document.getElementById('duration-slider');
            const durationValue = document.getElementById('duration-value');
            const dynamicDemo = document.getElementById('dynamic-demo');
            
            durationSlider.addEventListener('input', function() {
                const duration = this.value;
                durationValue.textContent = duration + 's';
                dynamicDemo.style.transitionDuration = duration + 's';
            });
            
            document.getElementById('test-duration').addEventListener('click', function() {
                dynamicDemo.style.transform = 'translateX(200px) rotate(180deg) scale(1.5)';
                setTimeout(function() {
                    dynamicDemo.style.transform = '';
                }, 100);
            });
            
            // Enhanced button interactions
            document.querySelectorAll('.demo-button').forEach(button => {
                button.addEventListener('click', function() {
                    const self = this;
                    this.style.transform = 'scale(0.95)';
                    setTimeout(function() {
                        self.style.transform = '';
                    }, 150);
                });
            });
            
            // Shape click interactions
            document.querySelectorAll('.morph-shape').forEach(shape => {
                shape.addEventListener('click', function() {
                    const self = this;
                    this.style.transform += ' rotate(360deg)';
                    setTimeout(function() {
                        self.style.transform = self.style.transform.replace(' rotate(360deg)', '');
                    }, 1000);
                });
            });
            
            // Auto-start demo
            setTimeout(function() {
                handleAction('start-race');
            }, 1000);
            
            // Preset timing function buttons
            const presetButtons = [
                { name: 'Bounce Effect', values: [0.68, -0.55, 0.27, 1.55] },
                { name: 'Elastic', values: [0.34, 1.56, 0.64, 1] },
                { name: 'Back In', values: [0.36, 0, 0.66, -0.56] },
                { name: 'Overshoot', values: [0.34, 1.56, 0.64, 1] }
            ];
            
            // Add preset buttons to bezier playground
            const presetContainer = document.createElement('div');
            presetContainer.style.textAlign = 'center';
            presetContainer.style.marginTop = '1rem';
            
            presetButtons.forEach(function(preset) {
                const btn = document.createElement('button');
                btn.className = 'card-demo-btn';
                btn.textContent = preset.name;
                btn.style.margin = '0.25rem';
                btn.addEventListener('click', function() {
                    document.getElementById('x1').value = preset.values[0];
                    document.getElementById('y1').value = preset.values[1];
                    document.getElementById('x2').value = preset.values[2];
                    document.getElementById('y2').value = preset.values[3];
                    updateBezier();
                });
                presetContainer.appendChild(btn);
            });
            
            document.querySelector('.bezier-playground').appendChild(presetContainer);
        });
    </script>
</body>
</html>

Timing Function Techniques

Built-in Functions

Use predefined timing functions for common animation patterns.

Custom Cubic Bezier

Create unique animation curves with cubic-bezier() functions.

Step Functions

Create discrete animations with step timing functions.

Material Design Curves

Use standardized timing functions for consistent UI animations.

Best Practices for Timing Functions

Effective Usage

  • Use ease-out for elements entering the screen
  • Use ease-in for elements exiting the screen
  • Use ease-in-out for elements moving within the screen
  • Use consistent timing functions across your UI
  • Test timing functions with your actual content
  • Consider using Material Design curves for consistency

Common Mistakes to Avoid

  • Using inappropriate timing functions for the context
  • Overusing complex cubic-bezier curves unnecessarily
  • Not testing with reduced motion preferences
  • Using different timing functions for similar interactions
  • Creating animations that are too slow or distracting
  • Not considering performance implications

Browser Support & Accessibility

  • CSS Timing Functions are supported in all modern browsers
  • Use feature queries (@supports) for progressive enhancement
  • Respect reduced motion preferences with media queries
  • Ensure animations don't cause accessibility issues
  • Test with screen readers and keyboard navigation
  • Provide fallbacks for older browsers when necessary

Ready to Try It Yourself?

Experiment with CSS Transition Timing Functions in our interactive editor. See your changes in real-time and build your understanding through practice.

< PreviousNext >