Back to Blog Posts

Modern CSS Techniques: Grid, Custom Properties, and Advanced Animations

10 min read
CSSWeb DesignFrontendAnimations

Introduction

CSS has evolved significantly over the past few years. Modern CSS features like Grid Layout, Custom Properties, and Container Queries have revolutionized how we build web interfaces. This guide explores these powerful techniques with practical examples.

CSS Grid: The Ultimate Layout System

Basic Grid Concepts

CSS Grid provides a two-dimensional layout system that's perfect for complex designs.

/* Basic grid setup */
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto;
  gap: 20px;
}

/* Responsive grid with auto-fit */
.responsive-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
}

/* Named grid areas for semantic layouts */
.page-layout {
  display: grid;
  grid-template-areas:
    "header header header"
    "sidebar main aside"
    "footer footer footer";
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

Advanced Grid Techniques

/* Complex magazine layout */
.magazine-layout {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-auto-rows: minmax(100px, auto);
  gap: 20px;
}

.feature-article {
  grid-column: span 8;
  grid-row: span 3;
}

.sidebar-content {
  grid-column: span 4;
  grid-row: span 2;
}

/* Masonry-like layout with Grid */
.masonry-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  grid-auto-rows: 10px;
  gap: 20px;
}

.masonry-item {
  grid-row-end: span var(--row-span);
}

/* Dynamic card layout */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(1rem, 2vw, 2rem);
  container-type: inline-size;
}

@container (min-width: 700px) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

CSS Custom Properties (CSS Variables)

Basic Custom Properties

/* Define custom properties */
:root {
  /* Colors */
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --text-color: #333333;
  --bg-color: #ffffff;
  
  /* Spacing */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 2rem;
  --spacing-xl: 4rem;
  
  /* Typography */
  --font-size-base: 16px;
  --font-scale: 1.25;
  --font-size-sm: calc(var(--font-size-base) / var(--font-scale));
  --font-size-lg: calc(var(--font-size-base) * var(--font-scale));
  --font-size-xl: calc(var(--font-size-lg) * var(--font-scale));
  
  /* Animations */
  --transition-speed: 300ms;
  --transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Using custom properties */
.button {
  background-color: var(--primary-color);
  color: var(--bg-color);
  padding: var(--spacing-sm) var(--spacing-md);
  font-size: var(--font-size-base);
  transition: all var(--transition-speed) var(--transition-easing);
}

.button:hover {
  background-color: color-mix(in srgb, var(--primary-color) 80%, black);
}

Dynamic Theming with Custom Properties

/* Light theme (default) */
:root {
  --bg-primary: #ffffff;
  --bg-secondary: #f5f5f5;
  --text-primary: #333333;
  --text-secondary: #666666;
  --accent: #3498db;
  --shadow: rgba(0, 0, 0, 0.1);
}

/* Dark theme */
[data-theme="dark"] {
  --bg-primary: #1a1a1a;
  --bg-secondary: #2a2a2a;
  --text-primary: #e0e0e0;
  --text-secondary: #a0a0a0;
  --accent: #5dade2;
  --shadow: rgba(0, 0, 0, 0.3);
}

/* Component using theme variables */
.card {
  background: var(--bg-secondary);
  color: var(--text-primary);
  box-shadow: 0 4px 6px var(--shadow);
  border-radius: 8px;
  padding: var(--spacing-lg);
}

/* JavaScript theme toggle */
// Theme toggle functionality
const toggleTheme = () => {
  const currentTheme = document.documentElement.getAttribute('data-theme');
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
  
  document.documentElement.setAttribute('data-theme', newTheme);
  localStorage.setItem('theme', newTheme);
};

// Apply saved theme on load
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);

Advanced Custom Property Patterns

/* Responsive spacing system */
:root {
  --space-unit: 1rem;
  --space-xs: calc(0.25 * var(--space-unit));
  --space-sm: calc(0.5 * var(--space-unit));
  --space-md: var(--space-unit);
  --space-lg: calc(2 * var(--space-unit));
  --space-xl: calc(4 * var(--space-unit));
}

@media (min-width: 768px) {
  :root {
    --space-unit: 1.25rem;
  }
}

/* Component-scoped custom properties */
.gradient-button {
  --gradient-start: var(--primary-color, #3498db);
  --gradient-end: var(--secondary-color, #2ecc71);
  --gradient-angle: 45deg;
  
  background: linear-gradient(
    var(--gradient-angle),
    var(--gradient-start),
    var(--gradient-end)
  );
  
  /* Hover state with modified gradient */
  &:hover {
    --gradient-angle: 135deg;
  }
}

/* Dynamic calculations with custom properties */
.responsive-grid {
  --min-column-width: 250px;
  --gap: 20px;
  --columns: 3;
  
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(
    auto-fit,
    minmax(
      max(
        var(--min-column-width),
        calc((100% - (var(--columns) - 1) * var(--gap)) / var(--columns))
      ),
      1fr
    )
  );
}

Container Queries

Container queries allow elements to adapt based on their container's size rather than the viewport.

/* Container setup */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Container query syntax */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 150px 1fr;
    gap: 20px;
  }
  
  .card-image {
    aspect-ratio: 1;
  }
}

@container card (min-width: 600px) {
  .card {
    grid-template-columns: 200px 1fr;
  }
  
  .card-content {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
}

/* Complex responsive component */
.dashboard-widget {
  container-type: inline-size;
  container-name: widget;
  background: var(--bg-secondary);
  border-radius: 8px;
  padding: var(--spacing-md);
}

@container widget (max-width: 300px) {
  .widget-header {
    font-size: var(--font-size-sm);
  }
  
  .widget-content {
    display: block;
  }
  
  .widget-stat {
    margin-bottom: var(--spacing-sm);
  }
}

@container widget (min-width: 500px) {
  .widget-content {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--spacing-md);
  }
}

Advanced CSS Animations

Keyframe Animations

/* Complex keyframe animation */
@keyframes morphing {
  0% {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
    transform: rotate(0deg);
  }
  
  25% {
    border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;
    transform: rotate(90deg) scale(1.1);
  }
  
  50% {
    border-radius: 50% 50% 50% 50% / 50% 50% 50% 50%;
    transform: rotate(180deg) scale(0.9);
  }
  
  75% {
    border-radius: 70% 30% 40% 60% / 60% 40% 70% 30%;
    transform: rotate(270deg) scale(1.05);
  }
  
  100% {
    border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
    transform: rotate(360deg);
  }
}

.morphing-shape {
  width: 200px;
  height: 200px;
  background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
  animation: morphing 8s ease-in-out infinite;
}

/* Staggered animations */
.stagger-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}

.stagger-item {
  opacity: 0;
  transform: translateY(50px);
  animation: fadeInUp 0.6s ease-out forwards;
  animation-delay: calc(var(--index) * 0.1s);
}

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

/* Usage with CSS custom properties */
.stagger-item:nth-child(1) { --index: 0; }
.stagger-item:nth-child(2) { --index: 1; }
.stagger-item:nth-child(3) { --index: 2; }
.stagger-item:nth-child(4) { --index: 3; }

Scroll-Triggered Animations

/* Scroll timeline animation */
@supports (animation-timeline: scroll()) {
  .parallax-container {
    position: relative;
    height: 100vh;
    overflow: hidden;
  }
  
  .parallax-layer {
    position: absolute;
    width: 100%;
    height: 120%;
    
    animation: parallax linear;
    animation-timeline: scroll();
    animation-range: 0vh 100vh;
  }
  
  .parallax-layer-1 {
    --parallax-speed: 5;
  }
  
  .parallax-layer-2 {
    --parallax-speed: 10;
  }
  
  .parallax-layer-3 {
    --parallax-speed: 15;
  }
  
  @keyframes parallax {
    to {
      transform: translateY(calc(var(--parallax-speed) * -10vh));
    }
  }
}

/* Intersection Observer for older browsers */
.fade-in-section {
  opacity: 0;
  transform: translateY(30px);
  transition: all 0.6s ease-out;
}

.fade-in-section.is-visible {
  opacity: 1;
  transform: translateY(0);
}

Advanced Transitions

/* Multi-property transitions with different timings */
.advanced-button {
  position: relative;
  padding: 12px 24px;
  background: var(--primary-color);
  color: white;
  border: none;
  border-radius: 4px;
  overflow: hidden;
  cursor: pointer;
  
  transition: 
    transform 200ms ease-out,
    box-shadow 300ms ease-out,
    background-color 400ms ease-out;
}

.advanced-button::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transform: translate(-50%, -50%);
  transition: width 0.6s, height 0.6s;
}

.advanced-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
  background-color: color-mix(in srgb, var(--primary-color) 80%, black);
}

.advanced-button:hover::before {
  width: 300px;
  height: 300px;
}

/* Page transitions */
.page-transition {
  animation: pageIn 0.6s ease-out;
}

@keyframes pageIn {
  from {
    opacity: 0;
    transform: translateX(-100px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}

Modern Layout Patterns

Fluid Typography

/* Clamp for responsive typography */
:root {
  --font-size-fluid: clamp(1rem, 2vw + 0.5rem, 1.5rem);
  --heading-size-fluid: clamp(2rem, 5vw + 1rem, 4rem);
}

.fluid-text {
  font-size: var(--font-size-fluid);
  line-height: 1.6;
}

.fluid-heading {
  font-size: var(--heading-size-fluid);
  line-height: 1.2;
}

/* Container query based typography */
.article {
  container-type: inline-size;
}

@container (min-width: 600px) {
  .article h2 {
    font-size: 2.5rem;
  }
  
  .article p {
    font-size: 1.125rem;
    columns: 2;
    column-gap: 2rem;
  }
}

Modern Card Layouts

/* Aspect ratio cards */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 2rem;
}

.card {
  display: grid;
  grid-template-rows: auto 1fr auto;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg-secondary);
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1);
}

.card-image {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  width: 100%;
}

.card-content {
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.card-footer {
  padding: 1rem 1.5rem;
  background: color-mix(in srgb, var(--bg-secondary) 90%, black);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

CSS Performance Optimization

Efficient Selectors

/* Avoid overly specific selectors */
/* Bad */
div.container > ul.list > li.item > a.link { }

/* Good */
.nav-link { }

/* Use CSS containment for performance */
.widget {
  contain: layout style;
}

/* Use will-change sparingly */
.animated-element {
  will-change: transform;
}

.animated-element:hover {
  transform: scale(1.1);
}

/* Remove will-change after animation */
.animated-element:not(:hover) {
  will-change: auto;
}

CSS Loading Strategies

<!-- Critical CSS inline -->
<style>
  /* Inline critical above-the-fold styles */
  body { margin: 0; font-family: system-ui; }
  .header { background: #333; color: white; }
</style>

<!-- Preload main stylesheet -->
<link rel="preload" href="/styles/main.css" as="style">
<link rel="stylesheet" href="/styles/main.css">

<!-- Load non-critical CSS asynchronously -->
<link rel="preload" href="/styles/animations.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

Accessibility Considerations

/* Focus visible for keyboard navigation */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-primary: #1a1a1a;
    --text-primary: #e0e0e0;
  }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
  :root {
    --primary-color: #0066cc;
    --text-color: #000000;
    --bg-color: #ffffff;
  }
}

/* Screen reader only content */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

Debugging CSS

/* Visual debugging helpers */
.debug * {
  outline: 1px solid red;
}

.debug-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 9999;
}

.debug-grid::before {
  content: '';
  grid-column: 1 / -1;
  background: repeating-linear-gradient(
    to right,
    rgba(255, 0, 0, 0.1) 0,
    rgba(255, 0, 0, 0.1) calc(100% / 12 - 20px),
    transparent calc(100% / 12 - 20px),
    transparent calc(100% / 12)
  );
}

Browser Support and Feature Detection

/* Feature detection with @supports */
@supports (display: grid) {
  .container {
    display: grid;
  }
}

@supports not (display: grid) {
  .container {
    display: flex;
    flex-wrap: wrap;
  }
}

/* Cascade layers for better organization */
@layer reset, base, components, utilities;

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

@layer base {
  :root {
    --primary-color: #3498db;
  }
}

@layer components {
  .button {
    background: var(--primary-color);
  }
}

@layer utilities {
  .mt-4 {
    margin-top: 1rem;
  }
}

Conclusion

Modern CSS provides powerful tools for creating sophisticated, maintainable, and performant web interfaces. Key takeaways:

  1. CSS Grid offers unparalleled control over layouts
  2. Custom Properties enable dynamic, themeable designs
  3. Container Queries create truly responsive components
  4. Modern animations can enhance user experience when used thoughtfully
  5. Performance matters - use efficient selectors and loading strategies
  6. Accessibility is essential - always consider user preferences

The key to mastering modern CSS is understanding when and how to use these features effectively. Start with the basics, experiment with advanced techniques, and always prioritize user experience.

Happy styling!