CSS Container Queries Layout System

Building a responsive card layout system using modern CSS Container Queries with fallback strategies for older browsers

CSS Container Queries Layout System
Modern CSS Techniques
Url
https://codepen.io/francescostella/pen/YPyjzYx
Period

CSS Container Queries Layout System

A practical exploration of CSS Container Queries to build truly responsive components that adapt based on their container size rather than viewport dimensions. This experiment demonstrates modern CSS techniques with progressive enhancement for browser compatibility.

Problem

Traditional responsive design relies on viewport-based media queries, which creates limitations:

Solution Architecture

Container Query Implementation

/* Base card component */
.card {
  container-type: inline-size;
  container-name: card;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1rem;
  background: white;
}

/* Responsive layouts based on container width */
@container card (min-width: 300px) {
  .card__content {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 1rem;
    align-items: center;
  }

  .card__image {
    width: 80px;
    height: 80px;
  }
}

@container card (min-width: 500px) {
  .card {
    padding: 1.5rem;
  }

  .card__content {
    grid-template-columns: 120px 1fr auto;
  }

  .card__image {
    width: 120px;
    height: 120px;
  }

  .card__actions {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }
}

@container card (min-width: 700px) {
  .card__content {
    grid-template-columns: 160px 1fr 200px;
  }

  .card__image {
    width: 160px;
    height: 160px;
  }

  .card__meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
  }
}

Progressive Enhancement Strategy

/* Fallback for browsers without container query support */
.card__content {
  display: block;
}

.card__image {
  width: 60px;
  height: 60px;
  float: left;
  margin-right: 1rem;
  margin-bottom: 0.5rem;
}

/* Feature detection and enhancement */
@supports (container-type: inline-size) {
  .card__image {
    float: none;
    margin: 0;
  }
}

JavaScript Enhancement

// Feature detection and polyfill loading
if (!CSS.supports('container-type: inline-size')) {
  // Load container query polyfill for older browsers
  import('https://cdn.skypack.dev/css-container-query-polyfill');

  // Add fallback class for different styling
  document.documentElement.classList.add('no-container-queries');
}

// Demo: Dynamic container resizing
class ContainerResizer {
  constructor(element) {
    this.element = element;
    this.isResizing = false;
    this.setupResizer();
  }

  setupResizer() {
    const resizer = document.createElement('div');
    resizer.className = 'resizer';
    resizer.innerHTML = '⟷';

    resizer.addEventListener('mousedown', (e) => {
      this.isResizing = true;
      this.startX = e.clientX;
      this.startWidth = this.element.offsetWidth;

      document.addEventListener('mousemove', this.handleResize);
      document.addEventListener('mouseup', this.stopResize);
    });

    this.element.appendChild(resizer);
  }

  handleResize = (e) => {
    if (!this.isResizing) return;

    const diff = e.clientX - this.startX;
    const newWidth = Math.max(200, this.startWidth + diff);
    this.element.style.width = `${newWidth}px`;
  };

  stopResize = () => {
    this.isResizing = false;
    document.removeEventListener('mousemove', this.handleResize);
    document.removeEventListener('mouseup', this.stopResize);
  };
}

// Initialize resizable containers
document.querySelectorAll('.resizable-container').forEach((container) => {
  new ContainerResizer(container);
});

HTML Structure

<div class="demo-layout">
  <div class="resizable-container" style="width: 400px;">
    <div class="card">
      <div class="card__content">
        <img class="card__image" src="https://picsum.photos/160" alt="Demo" />
        <div class="card__info">
          <h3 class="card__title">Responsive Product Card</h3>
          <p class="card__description">
            This card adapts its layout based on container width, not viewport
            size.
          </p>
          <div class="card__meta">
            <span class="card__price">$99.99</span>
            <span class="card__rating">★★★★☆</span>
          </div>
        </div>
        <div class="card__actions">
          <button class="btn btn-primary">Add to Cart</button>
          <button class="btn btn-secondary">Save</button>
        </div>
      </div>
    </div>
  </div>
</div>

Key Technical Insights

Container Query Advantages

  1. True Component Isolation: Components respond to their actual space
  2. Reusable Across Contexts: Same component works in sidebar, main content, or modal
  3. Better Grid Layouts: Dynamic columns based on available space
  4. Reduced CSS Complexity: No need for contextual modifier classes

Browser Support Strategy

/* Progressive enhancement approach */
@supports not (container-type: inline-size) {
  .card {
    /* Simplified layout for older browsers */
    display: block;
  }

  .card__content {
    display: flex;
    align-items: center;
    gap: 1rem;
  }
}

Performance Considerations

Real-World Applications

Dashboard Widgets

Perfect for admin dashboards where widgets can be:

E-commerce Product Cards

Automatically adapt from:

Content Management Systems

Components that work seamlessly in:

Browser Compatibility

BrowserContainer QueriesPolyfill Needed
Chrome 105+✅ NativeNo
Firefox 110+✅ NativeNo
Safari 16+✅ NativeNo
IE/Legacy❌ Not SupportedYes

Implementation Tips

Best Practices

  1. Always provide fallbacks for browsers without support
  2. Use meaningful container names for better debugging
  3. Test with dynamic content to ensure layouts don’t break
  4. Consider container query units (cqw, cqh) for typography scaling

Common Pitfalls

CodePen Demo Features

The live demo showcases:

Future Enhancements


This experiment demonstrates how container queries solve real layout problems while maintaining backward compatibility through progressive enhancement.