Chapter 21: CSS At-Rules & Modern CSS Features
CSS At-Rules (prefixed with @) instruct the browser rendering engine how to parse, import, encapsulate, scope, and structure stylesheets. Mastering conditional media queries, keyframes, font loading (@font-face), feature queries (@supports), container queries (@container), Houdini properties (@property), Cascade Layers (@layer), native DOM scoping (@scope), and entry transitions (@starting-style) creates enterprise-grade CSS architecture.
21.1 Comprehensive At-Rules Reference Guide
| At-Rule | Syntax Pattern | Primary Purpose & Key Advantage |
|---|---|---|
@import |
@import url("styles.css") layer(base); |
Imports an external CSS stylesheet (can optionally assign to a cascade layer). |
@font-face |
@font-face { font-family: 'Custom'; src: url(...); } |
Defines custom web font files and loading behaviors (font-display: swap). |
@media |
@media (width >= 768px) { ... } |
Applies CSS rules conditionally based on device viewport or screen capabilities. |
@keyframes |
@keyframes pulse { 0% { opacity: 0; } 100% { opacity: 1; } } |
Defines keyframe milestones for smooth CSS animations. |
@supports |
@supports (backdrop-filter: blur(5px)) { ... } |
Feature query testing browser property/selector support before applying styles. |
@container |
@container card (min-width: 400px) { ... } |
Component-level responsive queries checking direct parent container size. |
@layer |
@layer reset, base, components, utilities; |
Declares explicit Cascade Layer priority order to eliminate specificity wars. |
@scope |
@scope (.card) to (.card-slot) { ... } |
Scopes CSS selectors to a specific DOM subtree without leaking styles. |
@starting-style |
@starting-style { opacity: 0; transform: scale(0.9); } |
Defines initial entry state when elements transition from display: none. |
@property |
@property --angle { syntax: "<angle>"; ... } |
Registers typed custom CSS variables for smooth Houdini gradient animations. |
21.2 Cascade Layers (@layer) & Specificity Architecture
Cascade Layers allow developers to establish explicit priority order for CSS blocks regardless of selector specificity. Rules inside later-declared layers always override earlier-declared layers:
| Cascade Layer Rule | Syntax Pattern | Behavioral Impact |
|---|---|---|
| Layer Declaration Order | @layer reset, base, components, utilities; |
Establishes priority order from lowest (reset) to highest (utilities). |
| Nested Layers | @layer components.buttons { .btn { ... } } |
Sub-divides layer architecture using dot notation. |
| Unlayered Styles Rule | Unlayered rules declared outside any @layer |
CRITICAL: Unlayered CSS rules ALWAYS defeat layered CSS rules. |
<!DOCTYPE html>
<html>
<head>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h4 { color: #818cf8; margin: 0 0 10px; }
/* Establish Layer Precedence: reset -> components -> utilities */
@layer reset, components, utilities;
@layer reset {
/* High specificity ID selector in lower reset layer */
#main-btn { background: #ef4444; color: #fff; padding: 12px 24px; border: none; border-radius: 8px; font-weight: bold; }
}
@layer utilities {
/* Utility layer wins over reset layer despite lower selector specificity! */
.bg-indigo { background: #6366f1; cursor: pointer; }
}
</style>
</head>
<body>
<h4>@layer Specificity Precedence Demo</h4>
<p style="font-size:12px;color:#64748b;">Button has id="main-btn" (in reset layer) and class="bg-indigo" (in utilities layer):</p>
<button id="main-btn" class="bg-indigo">Styled by @layer utilities ✅</button>
</body>
</html>
21.3 CSS Native Scoping (@scope) & Donut Boundaries
The @scope at-rule limits selector matching strictly to a specific DOM subtree. By defining a "donut scope" with to (boundary), styles are prevented from bleeding into inner slotted child elements:
@scope (.media-card) to (.card-slot) {img { border-radius: 8px; } /* Styles ONLY images inside .media-card, ignoring images inside .card-slot! */}
<!DOCTYPE html>
<html>
<head>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h4 { color: #38bdf8; margin: 0 0 10px; }
/* @scope targets .scope-card but stops at .slot-boundary */
@scope (.scope-card) to (.slot-boundary) {
p { color: #38bdf8; font-weight: bold; }
}
</style>
</head>
<body>
<h4>@scope Donut Boundary Demo</h4>
<div class="scope-card" style="background:#1e293b;border:2px solid #334155;border-radius:12px;padding:16px;max-width:380px;">
<p style="margin:0 0 10px;">Scoped paragraph text (Styled blue by @scope)</p>
<div class="slot-boundary" style="background:#0f172a;padding:12px;border-radius:8px;">
<p style="margin:0;color:#94a3b8;font-weight:normal;">Inner slot text (Protected from @scope leakage!)</p>
</div>
</div>
</body>
</html>
21.4 CSS Entry Animations (@starting-style) Without JavaScript
Historically, animating elements when inserted into the DOM or transitioning from display: none to display: block required JavaScript timeouts. @starting-style defines the element's initial pre-render state for smooth CSS entrance transitions:
.dialog { transition: opacity 0.4s ease, display 0.4s allow-discrete; }@starting-style { .dialog { opacity: 0; transform: scale(0.9); } }
<!DOCTYPE html>
<html>
<head>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
h4 { color: #10b981; margin: 0 0 10px; }
.entry-card {
background: #1e293b; border: 2px solid #10b981; border-radius: 12px;
padding: 16px; max-width: 320px;
opacity: 1; transform: scale(1);
transition: opacity 0.6s ease, transform 0.6s ease;
}
/* Defines initial state when element first enters DOM */
@starting-style {
.entry-card {
opacity: 0;
transform: scale(0.85) translateY(15px);
}
}
</style>
</head>
<body>
<h4>@starting-style DOM Entry Transition Demo</h4>
<div class="entry-card">
<strong style="color:#34d399;">DOM Entry Animation</strong>
<p style="font-size:12px;color:#94a3b8;margin:4px 0 0;">Smoothly scales & fades in on page load without JavaScript!</p>
</div>
</body>
</html>
21.5 Interactive Sandbox — At-Rules, @layer, @scope & @starting-style
Test @layer priority ordering, @scope boundary limits, and @container queries live in the playground below: