@carmenansio
@carmenansio
@carmenansio
@carmenansio
@carmenansio
@carmenansio
@carmenansio
@carmenansio

Moving things

LottieFiles

@carmenansio
@carmenansio

Why motion matters

Personality, feedback, tells a story

@carmenansio

You hover something

and it reacts!

@carmenansio

What we usually see

Yep... we keep seeing this

@carmenansio
@carmenansio
button:hover {
  transform: scale(1.2);
}
@carmenansio
@carmenansio
@carmenansio
<a href="https://es.wikipedia.org/wiki/The_Legend_of_Zelda:_The_Wind_Waker" alt="Zelda Wind Waker card" target="_blank">
	<div class="card">
		<div class="wrapper">
			<img src="https://assets.codepen.io/527512/zelda_cover-image.webp" class="cover-image" />
		</div>
		<img src="https://assets.codepen.io/527512/zelda_title.webp" class="title" />
		<img src="https://assets.codepen.io/527512/zelda_character.webp" class="character" />
	</div>
</a>
@carmenansio
@carmenansio
:root {
	--card-height: 300px;
	--card-width: calc(var(--card-height) / 1.5);
}
@carmenansio
.card {
  width: var(--card-width);
  height: var(--card-height);
  perspective: 2500px;
}
@carmenansio
.wrapper {
  transition: all 0.5s;
  position: absolute;
  z-index: -1;
}

.card:hover .wrapper {
  transform: perspective(900px) translateY(-5%) rotateX(25deg);
  box-shadow: 2px 35px 32px -8px rgba(0, 0, 0, 0.75);
}
@carmenansio
.card:hover .title {
  transform: translate3d(0%, -50px, 100px);
}

.card:hover .character {
  opacity: 1;
  transform: translate3d(0%, -30%, 100px);
}
@carmenansio
@carmenansio
@carmenansio
<p>
	Design
	<span>Design</span>
	<span>Design</span>
	<span>Engineer</span>
</p>

The Tools

Modern CSS Power-Ups

@carmenansio
@carmenansio
.card:has(button:hover) {
  transform: scale(1.02);
}

:has() = contextual motion

@carmenansio
@carmenansio
@property --glow {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

@layer + @property = smoother control

@carmenansio
@carmenansio
nav a:nth-child(1) { transition-delay: 0ms; }
nav a:nth-child(2) { transition-delay: 50ms; }
nav a:nth-child(3) { transition-delay: 100ms; }

Micro delay = ✨magic✨

@carmenansio
@carmenansio
transition: all 0.4s cubic-bezier(.33, 1, .68, 1);

Timing curves that feel human

@carmenansio
@carmenansio
.card {
	width: 200px;
	height: 300px;
	background: linear-gradient(45deg, #ff6b6b, #ffde59);
	transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1), background 0.4s ease,
		box-shadow 0.4s ease;
	cursor: pointer;
	box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}

Timing curves that feel human

Real-world pattern

The animated card

@carmenansio
@carmenansio
  • a hover effect on the image (with :has())

  • a subtle background shift when the card becomes :focus-within

  • a transition-delay to let text fade in just a touch later

@carmenansio
@carmenansio
@carmenansio
<article class="blog-card">
  <div class="gradient-border"></div>
  <div class="card-content">
    <figure class="image-container">
      <img src="..." alt="The Future of AI" />
    </figure>
    <section class="content">
      <header class="content-text">
        <h2>Title</h2>
        <p>Description</p>
      </header>
      <footer class="card-footer">
        <span class="author">John Doe</span>
        <div class="arrow-container">
          <svg class="arrow-icon">...</svg>
        </div>
      </footer>
    </section>
  </div>
</article>
@carmenansio
.gradient-border {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 24px;
  background: linear-gradient(45deg, var(--gradient-1), var(--gradient-2), var(--gradient-3), var(--gradient-4));
  background-size: 300% 300%;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.blog-card:hover .gradient-border {
  opacity: 1;
  animation: gradientFlow 3s linear infinite;
}
@carmenansio
.image-container img {
  transition: transform 0.5s ease;
}

.blog-card:hover .image-container img {
  transform: scale(1.05);
}
@carmenansio
.author {
  background: linear-gradient(to right, #92ff38, #e5ff00);
  -webkit-background-clip: text;
  color: transparent;
}

.arrow-container, .arrow-icon {
  transition: all 0.3s ease;
}

.blog-card:hover .arrow-container {
  border-color: #92ff38;
}

.blog-card:hover .arrow-icon {
  color: #92ff38;
}
@carmenansio
@carmenansio
@carmenansio
body {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background: #222;
}

.box {
  position: relative;
  width: 400px;
  height: 300px;
}
@carmenansio
.box::before,
.box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-conic-gradient(
    from var(--a),
    #0f0, #ff0, #0ff, #f0f, #0ff
  );
  animation: rotating 4s linear infinite;
  border-radius: 20px;
}

.box::after {
  filter: blur(40px);
  opacity: 0.75;
}
@carmenansio
@property --a {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@keyframes rotating {
  0% { --a: 0deg; }
  100% { --a: 360deg; }
}
@carmenansio
@carmenansio
@carmenansio
body {
  background: #0e1217;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}
@carmenansio
.button.border {
  box-shadow: inset 0 0 0 2px #fff;
  position: relative;
  color: #fff;
}
@carmenansio
.border::before,
.border::after {
  position: absolute;
  width: 0;
  height: 0;
  border: 0 solid transparent;
  content: "";
}

.border:hover::before,
.border:hover::after {
  border-color: #16bdca;
  width: 100%;
  height: 100%;
}

The point is

CSS can do more than we think

@carmenansio
  • Better performance

  • Fewer dependencies

  • More accessible interactions

  • Less JS to maintain

@carmenansio

What CSS can actually do

New CSS powers ups

@carmenansio
@carmenansio
@carmenansio
.fade-in {
  opacity: 1;
  transition: opacity 0.5s;

  @starting-style {
    opacity: 0;
  }
}

CSS-Only Enter Animations with @starting-style

@carmenansio
button {
  translate: 0 0;
  scale: 1;
  rotate: 0deg;
  transition: scale 0.3s ease, 
    rotate 0.5s ease;
}

button:hover {
  scale: 1.2;
  rotate: 10deg;
}

Independent Transforms with translate, scale, rotate

@carmenansio
@carmenansio
.box {
  transform: translateX(0);
  transition: transform 2s linear(
    0, 0.03, 0.15 5%, 0.45, 0.82 15%, 1.05, 1
  );
}

.box:hover {
  transform: translateX(100px);
}

Simulated Springs with linear() easing

@carmenansio
.section {
  animation: fadeMove 1s ease forwards;
  animation-timeline: view();
}

@keyframes fadeMove {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Scroll-Linked Animations with animation-timeline

@carmenansio
@carmenansio
@carmenansio

Bonus: auto height animations with calc-size() (coming soon)

li {
  height: 0;
  transition: height 0.3s ease;
}

li.open {
  height: calc-size(auto);
}
@carmenansio
@carmenansio
html,
body {
  display: grid;
  min-height: 100%;
  background: #121212;
}

.wrap {
  --d: clamp(5em, 16vw, 15em);
  place-self: center;
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  padding: var(--d) 0;
  max-width: calc(6 * var(--d));
  perspective: calc(3 * var(--d));
  white-space: nowrap;
  scrollbar-color: crimson #212121;
}
@carmenansio
.move {
  display: inline-block;
  transform-style: preserve-3d;
}

figure {
  display: inline-block;
  scroll-snap-align: center;
  margin: 0;
  transform-style: preserve-3d;
}

img {
  width: var(--d);
  aspect-ratio: 1;
  -webkit-box-reflect: below 0.5em linear-gradient(#0000, #0004);
  animation: ry 1s cubic-bezier(0.32, 0, 0.68, 0) both,
             tz 1s ease-in both;
  animation-timeline: view(inline);
}
@carmenansio
@keyframes ry {
  0% {
    rotate: y -90deg;
  }
  50% {
    rotate: y 0deg;
    animation-timing-function: cubic-bezier(0.32, 1, 0.68, 1);
  }
  100% {
    rotate: y 90deg;
  }
}

@keyframes tz {
  0% {
    translate: -50% 0;
  }
  50% {
    translate: 0 0 calc(1.5 * var(--d));
    animation-timing-function: ease-out;
  }
  100% {
    translate: 50% 0;
  }
}

Wrap-up

What I want you to take away

@carmenansio
@carmenansio
@carmenansio

Make cool stuff, but enjoy the ride

@carmenansio
@carmenansio
@carmenansio

That's it!

Thanks

@carmenansio