Motion / Kinetic
Animation-first design where transitions, micro-interactions, and movement ARE the design — nothing sits still.
MCP
Code
:root {
--m-bg: #0f0f0f;
--m-surface: #171717;
--m-surface-2: #1f1f1f;
--m-orange: #ff3d00;
--m-orange-light: #ff6e40;
--m-text: #f0f0f0;
--m-text-muted: rgba(240, 240, 240, 0.5);
--m-border: rgba(255, 255, 255, 0.08);
--m-radius: 10px;
--font: "Plus Jakarta Sans", system-ui, sans-serif;
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background: var(--m-bg);
color: var(--m-text);
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* ── Animated bg gradient ── */
.bg-gradient {
position: fixed;
inset: 0;
z-index: 0;
background: linear-gradient(
135deg,
rgba(255, 61, 0, 0.06) 0%,
rgba(15, 15, 15, 0) 40%,
rgba(0, 229, 255, 0.04) 100%
);
background-size: 400% 400%;
animation: bgPulse 10s ease infinite;
pointer-events: none;
}
@keyframes bgPulse {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* ── Page ── */
.page {
position: relative;
z-index: 1;
max-width: 820px;
margin: 0 auto;
padding: 60px 24px 80px;
display: flex;
flex-direction: column;
gap: 56px;
}
/* ── Entrance animations ── */
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(40px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes wordIn {
from {
opacity: 0;
transform: translateY(60px) skewY(3deg);
}
to {
opacity: 1;
transform: translateY(0) skewY(0deg);
}
}
@keyframes badgeSlide {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.anim-up {
opacity: 0;
animation: slideUp 0.6s var(--ease-spring) var(--delay, 0s) forwards;
}
.word {
display: inline-block;
overflow: hidden;
opacity: 0;
animation: wordIn 0.7s var(--ease-spring) calc(var(--i) * 0.15s + 0.1s) forwards;
}
.anim-badge {
opacity: 0;
animation: badgeSlide 0.4s var(--ease-out) calc(var(--bi) * 0.07s + 1.1s) forwards;
}
/* ── Header ── */
.header {
text-align: center;
}
.header__eyebrow {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.14em;
color: var(--m-orange);
text-transform: uppercase;
margin-bottom: 20px;
}
.header__title {
font-size: clamp(52px, 10vw, 100px);
font-weight: 800;
line-height: 1;
letter-spacing: -0.04em;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
}
.header__title .accent {
color: var(--m-orange);
}
.header__tagline {
margin-top: 20px;
font-size: 17px;
color: var(--m-text-muted);
font-weight: 400;
line-height: 1.6;
}
/* ── Card ── */
.card-section {
display: flex;
justify-content: center;
}
.motion-card {
background: var(--m-surface);
border: 1px solid var(--m-border);
border-radius: 16px;
padding: 28px;
width: 100%;
max-width: 400px;
transition: transform 0.3s var(--ease-spring), box-shadow 0.3s ease;
cursor: default;
will-change: transform;
}
.motion-card:hover {
box-shadow: 0 24px 60px rgba(255, 61, 0, 0.12), 0 4px 20px rgba(0, 0, 0, 0.4);
}
.card__top {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 24px;
}
.avatar {
flex-shrink: 0;
border-radius: 10px;
overflow: hidden;
border: 2px solid var(--m-border);
}
.card__info {
flex: 1;
}
.card__name {
font-size: 17px;
font-weight: 700;
color: var(--m-text);
}
.card__role {
font-size: 13px;
color: var(--m-text-muted);
margin-top: 2px;
}
.card__badge {
font-size: 10px;
font-weight: 800;
letter-spacing: 0.1em;
color: var(--m-orange);
background: rgba(255, 61, 0, 0.12);
border: 1px solid rgba(255, 61, 0, 0.3);
padding: 4px 8px;
border-radius: 4px;
}
.card__stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
border-top: 1px solid var(--m-border);
padding-top: 20px;
}
.stat {
text-align: center;
}
.stat__value {
display: block;
font-size: 24px;
font-weight: 800;
color: var(--m-text);
letter-spacing: -0.03em;
}
.stat__label {
display: block;
font-size: 11px;
color: var(--m-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-top: 3px;
}
/* ── Controls ── */
.controls {
display: flex;
flex-direction: column;
gap: 28px;
}
/* ── Buttons ── */
.button-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.btn {
position: relative;
overflow: hidden;
font-family: var(--font);
font-size: 14px;
font-weight: 700;
padding: 13px 28px;
border: none;
border-radius: 8px;
cursor: pointer;
letter-spacing: 0.04em;
transition: transform 0.2s var(--ease-spring), background 0.2s ease, box-shadow 0.2s ease;
}
.btn__inner {
position: relative;
z-index: 1;
pointer-events: none;
}
.btn--primary {
background: var(--m-orange);
color: white;
}
.btn--primary:hover {
transform: scale(1.06);
background: var(--m-orange-light);
box-shadow: 0 8px 30px rgba(255, 61, 0, 0.45);
}
.btn--primary:active {
transform: scale(0.96);
}
.btn--secondary {
background: var(--m-surface-2);
color: var(--m-text);
border: 1px solid var(--m-border);
}
.btn--secondary:hover {
background: var(--m-text);
color: var(--m-bg);
transform: scale(1.04);
border-color: transparent;
}
.btn--secondary:active {
transform: scale(0.96);
}
.btn--ghost {
background: transparent;
color: var(--m-text);
border: 1.5px solid var(--m-orange);
}
.btn--ghost:hover {
background: var(--m-orange);
color: white;
transform: scale(1.04);
box-shadow: 0 6px 24px rgba(255, 61, 0, 0.3);
}
.btn--ghost:active {
transform: scale(0.96);
}
/* ── Floating label input ── */
.field {
position: relative;
width: 100%;
max-width: 400px;
}
.field__input {
width: 100%;
padding: 20px 16px 8px;
font-family: var(--font);
font-size: 15px;
font-weight: 500;
color: var(--m-text);
background: var(--m-surface);
border: 1.5px solid var(--m-border);
border-radius: 10px;
outline: none;
transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.field__input:focus {
border-color: var(--m-orange);
box-shadow: 0 0 0 3px rgba(255, 61, 0, 0.12);
}
.field__label {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 15px;
font-weight: 500;
color: var(--m-text-muted);
pointer-events: none;
transform-origin: left center;
transition: transform 0.25s var(--ease-spring), color 0.25s ease, font-size 0.25s ease;
}
.field__input:focus ~ .field__label,
.field__input:not(:placeholder-shown) ~ .field__label {
transform: translateY(-145%) scale(0.8);
color: var(--m-orange);
font-size: 13px;
}
/* ── Badges ── */
.badge-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.badge {
display: inline-block;
font-size: 12px;
font-weight: 700;
padding: 6px 14px;
border-radius: 6px;
background: var(--m-surface-2);
color: var(--m-text-muted);
border: 1px solid var(--m-border);
transition: color 0.2s ease, background 0.2s ease, transform 0.2s var(--ease-spring);
}
.badge:hover {
color: var(--m-orange);
background: rgba(255, 61, 0, 0.08);
border-color: rgba(255, 61, 0, 0.25);
transform: translateY(-2px);
}/* Motion / Kinetic — Interactive JS */
(function () {
"use strict";
// ── Elastic click animation on primary button ──
const primaryBtn = document.getElementById("primaryBtn");
if (primaryBtn) {
primaryBtn.addEventListener("click", () => {
primaryBtn.style.transition = "transform 0.08s ease";
primaryBtn.style.transform = "scale(0.92)";
setTimeout(() => {
primaryBtn.style.transition = "transform 0.5s cubic-bezier(0.34,1.56,0.64,1)";
primaryBtn.style.transform = "scale(1)";
setTimeout(() => {
primaryBtn.style.transition = "";
primaryBtn.style.transform = "";
}, 500);
}, 80);
});
}
// ── Mouse parallax on the motion card ──
const card = document.getElementById("motionCard");
if (card) {
document.addEventListener("mousemove", (e) => {
const rect = card.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const dx = (e.clientX - cx) / window.innerWidth;
const dy = (e.clientY - cy) / window.innerHeight;
// Max 8px parallax
const ox = dx * 8;
const oy = dy * 8;
card.style.transform = `translate(${ox}px, ${oy}px)`;
});
document.addEventListener("mouseleave", () => {
card.style.transform = "";
});
}
// ── IntersectionObserver: re-trigger entrance animations on scroll ──
const animItems = document.querySelectorAll(".anim-up, .word, .anim-badge");
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Reset animation by toggling class
const el = entry.target;
el.style.animationPlayState = "running";
}
});
},
{ threshold: 0.1 }
);
animItems.forEach((el) => observer.observe(el));
// ── Stat counter animation ──
function animateCounter(el, target, duration = 1000, suffix = "") {
const start = performance.now();
const isFloat = target % 1 !== 0;
const targetNum = parseFloat(target);
requestAnimationFrame(function tick(now) {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
// Ease out quad
const eased = 1 - (1 - progress) * (1 - progress);
const current = eased * targetNum;
el.textContent = (isFloat ? current.toFixed(1) : Math.floor(current)) + suffix;
if (progress < 1) requestAnimationFrame(tick);
});
}
// Parse stat values and animate them in
const statValues = document.querySelectorAll(".stat__value");
statValues.forEach((el) => {
const raw = el.textContent.trim();
let num, suffix;
if (raw.endsWith("k")) {
num = parseFloat(raw);
suffix = "k";
} else if (raw.endsWith("%")) {
num = parseFloat(raw);
suffix = "%";
} else {
num = parseFloat(raw);
suffix = "";
}
el.textContent = "0" + suffix;
// Trigger after entrance animation delay
setTimeout(() => animateCounter(el, num, 1200, suffix), 900);
});
// ── All buttons: ripple effect ──
document.querySelectorAll(".btn").forEach((btn) => {
btn.addEventListener("click", function (e) {
const ripple = document.createElement("span");
const rect = btn.getBoundingClientRect();
const size = Math.max(rect.width, rect.height) * 2;
ripple.style.cssText = `
position: absolute;
width: ${size}px;
height: ${size}px;
border-radius: 50%;
background: rgba(255,255,255,0.15);
top: ${e.clientY - rect.top - size / 2}px;
left: ${e.clientX - rect.left - size / 2}px;
transform: scale(0);
animation: rippleKinetic 0.5s ease-out forwards;
pointer-events: none;
z-index: 0;
`;
if (!document.getElementById("ripple-style")) {
const style = document.createElement("style");
style.id = "ripple-style";
style.textContent = `
@keyframes rippleKinetic {
to { transform: scale(1); opacity: 0; }
}
`;
document.head.appendChild(style);
}
btn.appendChild(ripple);
setTimeout(() => ripple.remove(), 500);
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
<title>Motion / Kinetic — Design Style</title>
</head>
<body>
<!-- Animated bg gradient layer -->
<div class="bg-gradient" aria-hidden="true"></div>
<div class="page">
<!-- Header — words animate in individually -->
<header class="header" id="header">
<div class="header__eyebrow">DESIGN SYSTEM</div>
<h1 class="header__title">
<span class="word" style="--i:0">Motion</span>
<span class="word accent" style="--i:1">Kinetic</span>
</h1>
<p class="header__tagline anim-up" style="--delay:0.55s">
Animation-first design — nothing<br />sits still.
</p>
</header>
<!-- Profile card -->
<section class="card-section">
<div class="motion-card anim-up" style="--delay:0.7s" id="motionCard">
<div class="card__top">
<div class="avatar">
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="52" height="52" rx="8" fill="#1a1a1a"/>
<circle cx="26" cy="19" r="9" fill="#FF3D00" opacity="0.85"/>
<path d="M8 44c0-9.941 8.059-18 18-18s18 8.059 18 18" fill="#FF3D00" opacity="0.5"/>
</svg>
</div>
<div class="card__info">
<h2 class="card__name">Jordan Kinley</h2>
<p class="card__role">Creative Director</p>
</div>
<div class="card__badge">PRO</div>
</div>
<div class="card__stats">
<div class="stat">
<span class="stat__value">1.2k</span>
<span class="stat__label">Projects</span>
</div>
<div class="stat">
<span class="stat__value">98%</span>
<span class="stat__label">On Time</span>
</div>
<div class="stat">
<span class="stat__value">48k</span>
<span class="stat__label">Fans</span>
</div>
</div>
</div>
</section>
<!-- Buttons -->
<section class="controls anim-up" style="--delay:0.85s">
<div class="button-row">
<button class="btn btn--primary" id="primaryBtn">
<span class="btn__inner">Launch</span>
</button>
<button class="btn btn--secondary">
<span class="btn__inner">Explore</span>
</button>
<button class="btn btn--ghost">
<span class="btn__inner">Preview</span>
</button>
</div>
<!-- Floating label input -->
<div class="field anim-up" style="--delay:1s">
<input class="field__input" id="motionInput" type="text" placeholder=" " />
<label class="field__label" for="motionInput">Your creative brief</label>
</div>
<!-- Badges -->
<div class="badge-row">
<span class="badge anim-badge" style="--bi:0">Motion</span>
<span class="badge anim-badge" style="--bi:1">Kinetic</span>
<span class="badge anim-badge" style="--bi:2">Animated</span>
<span class="badge anim-badge" style="--bi:3">Dark UI</span>
</div>
</section>
</div>
<script src="script.js"></script>
</body>
</html>Motion / Kinetic
In kinetic design, animation is not decoration — it is the primary design language. Every element earns its place by moving: headings slide in word by word, cards rise from below on scroll, buttons respond elastically to clicks, and a slow gradient pulse breathes life into the background. The absence of motion would make this style feel incomplete, like a song without rhythm.
The dark foundation (#0F0F0F) puts maximum focus on the kinetic orange accent (#FF3D00), which pops against the near-black surface with high-energy contrast. Typography is bold and heavy — 'Plus Jakarta Sans' at large weights — because the text itself needs to feel dynamic even when static. Every transition uses carefully chosen easing curves: cubic-bezier(0.34, 1.56, 0.64, 1) for spring-like overshoots, cubic-bezier(0.25, 0.46, 0.45, 0.94) for smooth exits.
Staggered animation-delay values create a choreographed entrance sequence where elements appear one after another, giving the impression of a performance rather than a page load. IntersectionObserver ensures that animations replay as sections scroll into view.
Key characteristics
- Dark
#0F0F0Fbackground with kinetic orange#FF3D00accent - Every element has a CSS entrance animation with staggered delay
- Floating label inputs with
transform-based label lift on focus - Elastic scale + color-flip hover states on all interactive elements
- Subtle animated gradient background shift (10s cycle)
- Mouse-position parallax on the profile card (max 8px offset)
When to use it
Perfect for creative agency portfolios, game launchers, entertainment products, and any brand that wants to communicate energy, speed, and modernity. The dark, high-contrast palette and constant movement demand full attention — avoid for utility-dense enterprise dashboards or long-form reading experiences.