Skip to main content
Back to Blog
Tutorials
3 min read
December 7, 2024

How to Build Animated CSS Components Without JavaScript

Create animated UI components using pure CSS with transitions, keyframes, and modern features like :has(), container queries, and scroll-driven animations.

Ryel Banfield

Founder & Lead Developer

Modern CSS can handle many interactive patterns that previously required JavaScript. Here are production-ready animated components.

1. Animated Accordion

<details class="accordion">
  <summary>How does billing work?</summary>
  <div class="accordion-content">
    <p>We bill monthly based on your plan. You can upgrade or downgrade at any time.</p>
  </div>
</details>
.accordion {
  border: 1px solid #e5e7eb;
  border-radius: 0.5rem;
  overflow: hidden;
}

.accordion summary {
  padding: 1rem;
  cursor: pointer;
  font-weight: 500;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.accordion summary::after {
  content: "+";
  font-size: 1.25rem;
  transition: transform 0.2s ease;
}

.accordion[open] summary::after {
  transform: rotate(45deg);
}

.accordion-content {
  padding: 0 1rem 1rem;
  animation: slideDown 0.2s ease;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-0.5rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

2. CSS-Only Modal with :target

<a href="#modal">Open Modal</a>

<div id="modal" class="modal-backdrop">
  <div class="modal">
    <h2>Modal Title</h2>
    <p>This modal is built with pure CSS using the :target pseudo-class.</p>
    <a href="#" class="modal-close">Close</a>
  </div>
</div>
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0);
  display: grid;
  place-items: center;
  pointer-events: none;
  transition: background 0.3s ease;
  z-index: 50;
}

.modal-backdrop:target {
  background: rgb(0 0 0 / 0.5);
  pointer-events: auto;
}

.modal {
  background: white;
  border-radius: 0.75rem;
  padding: 2rem;
  max-width: 28rem;
  width: 90%;
  transform: scale(0.95) translateY(1rem);
  opacity: 0;
  transition: all 0.3s ease;
}

.modal-backdrop:target .modal {
  transform: scale(1) translateY(0);
  opacity: 1;
}

.modal-close {
  display: inline-block;
  margin-top: 1rem;
  color: #6b7280;
  text-decoration: none;
}

3. Animated Tooltip

<span class="tooltip-wrapper">
  Hover me
  <span class="tooltip">This is a tooltip</span>
</span>
.tooltip-wrapper {
  position: relative;
  cursor: help;
}

.tooltip {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  transform: translateX(-50%) translateY(0.25rem);
  background: #1f2937;
  color: white;
  padding: 0.375rem 0.75rem;
  border-radius: 0.375rem;
  font-size: 0.75rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: all 0.2s ease;
}

.tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-top-color: #1f2937;
}

.tooltip-wrapper:hover .tooltip {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

4. Animated Tab Navigation

<div class="tabs">
  <input type="radio" name="tab" id="tab1" checked />
  <input type="radio" name="tab" id="tab2" />
  <input type="radio" name="tab" id="tab3" />

  <nav class="tab-nav">
    <label for="tab1">Overview</label>
    <label for="tab2">Features</label>
    <label for="tab3">Pricing</label>
    <div class="tab-indicator"></div>
  </nav>

  <div class="tab-panels">
    <div class="tab-panel">Overview content here.</div>
    <div class="tab-panel">Features content here.</div>
    <div class="tab-panel">Pricing content here.</div>
  </div>
</div>
.tabs {
  width: 100%;
}

.tabs input[type="radio"] {
  display: none;
}

.tab-nav {
  display: flex;
  position: relative;
  border-bottom: 2px solid #e5e7eb;
}

.tab-nav label {
  flex: 1;
  padding: 0.75rem;
  text-align: center;
  cursor: pointer;
  font-weight: 500;
  color: #6b7280;
  transition: color 0.2s;
}

.tab-indicator {
  position: absolute;
  bottom: -2px;
  height: 2px;
  width: calc(100% / 3);
  background: #2563eb;
  transition: transform 0.3s ease;
}

#tab1:checked ~ .tab-nav .tab-indicator { transform: translateX(0); }
#tab2:checked ~ .tab-nav .tab-indicator { transform: translateX(100%); }
#tab3:checked ~ .tab-nav .tab-indicator { transform: translateX(200%); }

#tab1:checked ~ .tab-nav label[for="tab1"],
#tab2:checked ~ .tab-nav label[for="tab2"],
#tab3:checked ~ .tab-nav label[for="tab3"] {
  color: #2563eb;
}

.tab-panels {
  overflow: hidden;
}

.tab-panel {
  display: none;
  padding: 1.5rem 0;
  animation: fadeIn 0.3s ease;
}

#tab1:checked ~ .tab-panels .tab-panel:nth-child(1),
#tab2:checked ~ .tab-panels .tab-panel:nth-child(2),
#tab3:checked ~ .tab-panels .tab-panel:nth-child(3) {
  display: block;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: translateY(0); }
}

5. Staggered List Animation

.stagger-list li {
  opacity: 0;
  transform: translateY(1rem);
  animation: slideUp 0.4s ease forwards;
}

.stagger-list li:nth-child(1) { animation-delay: 0s; }
.stagger-list li:nth-child(2) { animation-delay: 0.05s; }
.stagger-list li:nth-child(3) { animation-delay: 0.1s; }
.stagger-list li:nth-child(4) { animation-delay: 0.15s; }
.stagger-list li:nth-child(5) { animation-delay: 0.2s; }
.stagger-list li:nth-child(6) { animation-delay: 0.25s; }

@keyframes slideUp {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

6. Scroll-Driven Progress Bar

.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: #2563eb;
  transform-origin: left;
  animation: scaleProgress auto linear;
  animation-timeline: scroll();
}

@keyframes scaleProgress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

7. Animated Gradient Border

.gradient-border {
  position: relative;
  border-radius: 0.75rem;
  padding: 2px;
  background: linear-gradient(
    var(--angle, 0deg),
    #3b82f6,
    #8b5cf6,
    #ec4899,
    #3b82f6
  );
  animation: rotate 3s linear infinite;
}

.gradient-border > * {
  background: white;
  border-radius: calc(0.75rem - 2px);
  padding: 1.5rem;
}

@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

@keyframes rotate {
  to { --angle: 360deg; }
}

8. Animated Card Hover

.card-hover {
  border: 1px solid #e5e7eb;
  border-radius: 0.75rem;
  padding: 1.5rem;
  transition: all 0.3s ease;
  cursor: pointer;
}

.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px -8px rgb(0 0 0 / 0.1);
  border-color: #3b82f6;
}

.card-hover h3 {
  transition: color 0.2s ease;
}

.card-hover:hover h3 {
  color: #2563eb;
}

9. Loading Skeleton

.skeleton {
  background: linear-gradient(
    90deg,
    #f3f4f6 25%,
    #e5e7eb 50%,
    #f3f4f6 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 0.375rem;
}

.skeleton-text {
  height: 1rem;
  margin-bottom: 0.5rem;
}

.skeleton-text:last-child {
  width: 60%;
}

.skeleton-avatar {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

10. View Transition for Page Navigation

@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: fadeOut 0.2s ease;
}

::view-transition-new(root) {
  animation: fadeIn 0.3s ease;
}

@keyframes fadeOut {
  to { opacity: 0; }
}

@keyframes fadeIn {
  from { opacity: 0; }
}

Need Polished UI Design?

We create beautiful, performant interfaces with smooth animations and modern CSS. Contact us to elevate your design.

CSSanimationstransitionsno JavaScripttutorial

Ready to Start Your Project?

RCB Software builds world-class websites and applications for businesses worldwide.

Get in Touch

Related Articles