CSS Animations Website Markup

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Website markup with CSS animations

CSS animations in the browser execute on the compositor thread — a separate thread that doesn't block JavaScript and rendering. Properly written animations don't cause layout recalculation and work at 60 fps even on mobile devices. Poorly written ones kill performance, cause jank, and frustrate users.

CSS transitions vs CSS animations: choosing the right tool

CSS transitions — for states (hover, focus, active). Declarative, concise:

.button {
  background-color: #2563eb;
  transform: translateY(0);
  transition:
    background-color 200ms ease,
    transform 150ms ease,
    box-shadow 200ms ease;
}

.button:hover {
  background-color: #1d4ed8;
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgb(37 99 235 / 0.4);
}

CSS animations (@keyframes) — for autonomous, looped, or multi-step movements:

@keyframes pulse-ring {
  0% {
    transform: scale(0.8);
    opacity: 0.8;
  }
  70% {
    transform: scale(1.4);
    opacity: 0;
  }
  100% {
    transform: scale(1.4);
    opacity: 0;
  }
}

.live-indicator::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: currentColor;
  animation: pulse-ring 1.8s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
}

Only compositor-safe properties

Only properties that don't cause reflow/repaint should be animated:

Property Composite Safe
transform Yes Yes
opacity Yes Yes
filter Partial Cautiously
width, height No No, use scale()
top, left No No, use translate()
background-color No Only via transition
clip-path Partial Yes, modern browsers

Rule: move via translate, scale via scale, rotate via rotate — never top/left/width/height in @keyframes.

will-change: when to apply

/* Correct — on a specific element, remove after animation */
.modal-overlay {
  will-change: opacity;
}

.modal-overlay.is-visible {
  will-change: auto; /* Free up resources after */
}

/* Incorrect — on everything */
* {
  will-change: transform; /* Will exhaust GPU memory */
}

Animation patterns

Skeleton loading

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

.skeleton {
  background: linear-gradient(
    90deg,
    #f0f0f0 25%,
    #e0e0e0 50%,
    #f0f0f0 75%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s ease-in-out infinite;
  border-radius: 4px;
}

Staggered list entrance

.list-item {
  opacity: 0;
  transform: translateY(16px);
  animation: slide-up 400ms ease forwards;
}

@keyframes slide-up {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Stagger via CSS custom property */
.list-item:nth-child(1) { animation-delay: calc(0 * 80ms); }
.list-item:nth-child(2) { animation-delay: calc(1 * 80ms); }
.list-item:nth-child(3) { animation-delay: calc(2 * 80ms); }

/* Or via inline style from JS/templater */
/* style="--index: 3" → animation-delay: calc(var(--index) * 80ms) */

Loading spinner

@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 20px;
  height: 20px;
  border: 2px solid rgb(0 0 0 / 0.1);
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 600ms linear infinite;
}

Hero section with parallax effect via CSS

.hero {
  perspective: 1px;
  overflow: hidden;
  height: 100svh;
}

.hero-bg {
  transform: translateZ(-1px) scale(2);
  /* Parallax without JavaScript, only via CSS perspective */
}

Accessibility: prefers-reduced-motion

Mandatory for any project:

/* Basic animation */
.notification {
  animation: bounce-in 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Disable for users with vestibular disorders */
@media (prefers-reduced-motion: reduce) {
  .notification {
    animation: fade-in 200ms ease;
  }

  /* Global acceleration of all animations */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Intersection Observer + CSS classes

Animations on viewport appearance — without heavy libraries:

// Minimalist intersection observer
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        observer.unobserve(entry.target); // Once only
      }
    });
  },
  { threshold: 0.15 }
);

document.querySelectorAll('[data-animate]').forEach((el) => observer.observe(el));
[data-animate] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 500ms ease, transform 500ms ease;
}

[data-animate].is-visible {
  opacity: 1;
  transform: translateY(0);
}

Performance: checklist

  • transform and opacity — only properties in @keyframes for movement
  • will-change only on elements with heavy animation, remove after
  • animation-fill-mode: both instead of duplicating initial state
  • No more than 20–30 simultaneously animated elements on the page
  • prefers-reduced-motion covers all animations
  • Test in Chrome DevTools → Performance → Rendering → Paint flashing

Timeline

Basic CSS transitions (hover states, modal appearances, fade effects): included in basic markup cost. Custom animations (skeleton, staggered lists, hero animations, parallax): 0.5–1 day depending on quantity and scene complexity.