CSS At-Rules - Controlling CSS Behavior

What You'll Learn

  • Use @media for responsive design with media queries
  • Apply @supports for progressive enhancement and feature detection
  • Create animations with @keyframes and control their behavior
  • Organize styles with @layer to control cascade priority
  • Scope styles with @scope to prevent style leakage
  • Build component-responsive layouts with @container queries
  • Register typed custom properties with @property for animations
  • Load custom fonts with @font-face and optimize display
  • Understand other at-rules: @import, @charset, @namespace, @page, @counter-style

Introduction to At-Rules

CSS at-rules are statements that begin with @ and instruct CSS how to behave. Unlike regular CSS rules that select and style elements, at-rules control broader aspects: importing stylesheets, defining animations, querying device features, organizing cascade layers, and more.

At-rules fall into several categories:

  • Conditional - @media, @supports, @container (apply styles conditionally)
  • Organizational - @layer, @scope, @import (organize and control cascade)
  • Definitional - @keyframes, @font-face, @property, @counter-style (define reusable assets)
  • Metadata - @charset, @namespace (provide information about the stylesheet)
  • Print - @page (control printed output)

@media - Media Queries

Media queries apply CSS conditionally based on device characteristics like viewport width, orientation, color scheme preferences, and more. They're the foundation of responsive design.

/* Width-based responsive design */ @media (max-width: 768px) { .sidebar { display: none; } } @media (min-width: 769px) { .content { display: grid; grid-template-columns: 300px 1fr; } } /* Color scheme preference */ @media (prefers-color-scheme: dark) { body { background: #1a1a1a; color: #f0f0f0; } } /* Reduced motion for accessibility */ @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } } /* Print styles */ @media print { .no-print { display: none; } body { font-size: 12pt; } } /* Orientation */ @media (orientation: landscape) { .hero { height: 60vh; } }

@keyframes - CSS Animations

Define the intermediate steps in a CSS animation sequence. You specify styles at various points (0%, 50%, 100%) and the browser smoothly interpolates between them.

/* Simple from-to animation */ @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* Multi-step animation with percentages */ @keyframes pulse { 0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7); } 50% { transform: scale(1.1); box-shadow: 0 0 0 20px rgba(102, 126, 234, 0); } } /* Complex multi-stage animation */ @keyframes rainbow { 0% { background: #667eea; } 25% { background: #16a34a; } 50% { background: #f59e0b; } 75% { background: #dc2626; } 100% { background: #667eea; } } /* Apply animation */ .element { animation: fadeIn 1s ease-out; } .pulse-element { animation: pulse 2s infinite; } .rainbow-element { animation: rainbow 4s ease-in-out infinite alternate; }

@supports - Feature Queries

Conditionally apply CSS based on whether the browser supports a specific feature. Perfect for progressive enhancement - provide fallbacks for older browsers while using modern features in newer ones.

/* Simple feature detection */ @supports (display: grid) { .container { display: grid; grid-template-columns: repeat(3, 1fr); } } @supports not (display: grid) { .container { display: flex; flex-wrap: wrap; } } /* Combined conditions with AND */ @supports (display: flex) and (gap: 1rem) { .flex-container { display: flex; gap: 1rem; /* Only use gap if both features supported */ } } /* Alternative conditions with OR */ @supports (display: grid) or (display: flex) { .layout { /* Use modern layout if either is supported */ } } /* Progressive enhancement example */ .card { background: white; /* Fallback */ } @supports (backdrop-filter: blur(10px)) { .card { background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(10px); } }

@layer - Cascade Layers

Explicitly control the cascade order of your styles, making specificity more predictable. Layers defined earlier have lower priority than layers defined later, regardless of selector specificity.

/* Define layer order upfront */ @layer reset, base, components, utilities; /* Reset layer (lowest priority) */ @layer reset { * { margin: 0; padding: 0; box-sizing: border-box; } } /* Base layer */ @layer base { body { font-family: system-ui, sans-serif; line-height: 1.6; } h1 { font-size: 2rem; } } /* Components layer */ @layer components { .button { padding: 0.5rem 1rem; background: blue; color: white; } } /* Utilities layer (highest priority in layers) */ @layer utilities { .mt-4 { margin-top: 1rem; } .text-center { text-align: center; } } /* Unlayered styles have highest priority! */ .special-override { background: red; /* Beats everything in layers */ }

@scope - Scoped Styles

Limit CSS rules to a specific part of the DOM, preventing style leakage. Provides style encapsulation without Shadow DOM, perfect for component-based design.

/* Scope styles to .card */ @scope (.card) { h2 { color: blue; font-size: 1.25rem; } p { margin: 1rem 0; } button { background: green; } } /* Scope with boundary - excludes .card-footer */ @scope (.card) to (.card-footer) { /* Styles apply to .card descendants, but NOT to anything inside .card-footer */ p { color: gray; } } /* Different scope for different component */ @scope (.sidebar) { h2 { color: purple; /* Won't affect .card h2 */ } }

@container - Container Queries

Apply styles based on a parent container's size, not the viewport. Enables true component-based responsive design - the same component adapts differently in a sidebar vs. main content area.

/* Define container */ .sidebar { container-type: inline-size; container-name: sidebar; } /* Query the container */ @container sidebar (min-width: 400px) { .card { display: grid; grid-template-columns: 1fr 1fr; } } @container sidebar (max-width: 399px) { .card { display: block; } } /* Container query units */ .card-title { font-size: clamp(1rem, 5cqi, 2rem); /* cqi = container inline size */ } /* Unnamed container */ .wrapper { container-type: inline-size; } @container (min-width: 600px) { .item { flex-direction: row; } }

@property - Typed Custom Properties

Register custom CSS properties with explicit types, enabling animations, better browser optimization, and validation. Without @property, custom properties are just strings and can't be animated!

/* Register typed custom properties */ @property --gradient-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; } @property --brand-color { syntax: '<color>'; inherits: false; initial-value: #667eea; } @property --progress { syntax: '<percentage>'; inherits: false; initial-value: 0%; } /* Now you can animate them! */ .element { --brand-color: #667eea; background: var(--brand-color); transition: --brand-color 1s ease; } .element:hover { --brand-color: #dc2626; /* Smoothly transitions! */ } /* Animate gradient rotation */ .gradient-box { --gradient-angle: 0deg; background: linear-gradient(var(--gradient-angle), blue, purple); animation: rotateGradient 3s infinite linear; } @keyframes rotateGradient { to { --gradient-angle: 360deg; } }

@font-face - Custom Fonts

Define custom fonts to be downloaded and used in your design. Load web fonts from files or reference local system fonts.

/* Load web font from files */ @font-face { font-family: 'CustomFont'; src: url('/fonts/custom-font.woff2') format('woff2'), url('/fonts/custom-font.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; /* Show fallback while loading */ } /* Load bold variant */ @font-face { font-family: 'CustomFont'; src: url('/fonts/custom-font-bold.woff2') format('woff2'); font-weight: bold; font-style: normal; font-display: swap; } /* Use local system font as fallback */ @font-face { font-family: 'SystemSerif'; src: local('Georgia'), local('Times New Roman'); font-display: swap; } /* Apply the font */ body { font-family: 'CustomFont', sans-serif; }

Other At-Rules

Several other at-rules serve specific purposes in CSS:

@import - Import Stylesheets

/* Import external stylesheet */ @import url('base.css'); @import url('print.css') print; @import url('mobile.css') screen and (max-width: 768px); /* Import into a layer */ @import url('theme.css') layer(theme); /* Import with supports */ @import url('modern.css') supports(display: grid);

@charset - Character Encoding

@charset "UTF-8"; /* Now you can use any Unicode characters */ .element::before { content: "★ ♥ © € → ←"; }

@page - Print Page Rules

/* Default page margins */ @page { size: letter; margin: 1in; } /* First page specific */ @page :first { margin-top: 2in; } /* Left and right pages (for books) */ @page :left { margin-left: 1.5in; margin-right: 1in; } @page :right { margin-left: 1in; margin-right: 1.5in; } /* Named pages */ @page invoice { size: A4 landscape; margin: 0.5in; } .invoice-page { page: invoice; /* Use named page */ }

@namespace - XML Namespaces

/* Rare in modern development */ @namespace url(http://www.w3.org/1999/xhtml); @namespace svg url(http://www.w3.org/2000/svg); /* Target SVG elements specifically */ svg|a { fill: blue; } /* Target HTML elements */ |a { color: blue; }

@counter-style - Custom List Markers

/* Define custom counter */ @counter-style emoji-numbers { system: cyclic; symbols: "1️⃣" "2️⃣" "3️⃣" "4️⃣" "5️⃣"; suffix: " "; } @counter-style custom-roman { system: additive; additive-symbols: 1000 M, 900 CM, 500 D, 400 CD, 100 C, 90 XC, 50 L, 40 XL, 10 X, 9 IX, 5 V, 4 IV, 1 I; } ol.custom { list-style: emoji-numbers; }

At-Rules Reference

Complete overview of CSS at-rules with browser support:

At-Rule Purpose Browser Support
@media Media queries for responsive design ✅ Excellent - All browsers
@supports Feature queries and detection ✅ Excellent - All modern browsers
@keyframes Define CSS animations ✅ Excellent - All browsers
@font-face Load custom web fonts ✅ Excellent - All browsers
@import Import external stylesheets ✅ Excellent - All browsers
@layer Cascade layer control ⚠️ Chrome/Edge 99+, Safari 15.4+, Firefox 97+
@scope Scoped styles ⚠️ Chrome/Edge 118+, Safari 17.4+ (not Firefox)
@container Container queries ⚠️ Chrome/Edge 105+, Safari 16+, Firefox 110+
@property Typed custom properties ⚠️ Chrome/Edge 85+, Safari 16.4+ (not Firefox)
@page Print page rules ✅ Good - Most browsers
@charset Character encoding ✅ Excellent - All browsers
@namespace XML/SVG namespaces ✅ Good - Most browsers
@counter-style Custom list markers ✅ Good - Firefox, Safari 17+

Key Takeaways

  • @media is essential for responsive design - width, orientation, color-scheme, reduced-motion, print
  • @keyframes define CSS animations with percentage-based or from/to keyframes
  • @supports enables progressive enhancement - detect features and provide fallbacks
  • @layer organizes cascade priority - earlier layers have lower priority than later layers
  • @scope prevents style leakage by limiting rules to DOM subtrees
  • @container enables component-responsive design based on container size, not viewport
  • @property registers typed custom properties enabling animations and validation
  • @font-face loads custom fonts - use font-display: swap for performance
  • @import imports stylesheets but blocks parallel loading - prefer <link> tags
  • Production-ready: @media, @supports, @keyframes, @font-face, @import, @charset, @page
  • Modern (use with feature detection): @layer, @scope, @container, @property
  • Unlayered styles have higher priority than any @layer
  • Container queries work on container size, not viewport - perfect for reusable components