← Back to CSS Course | Chapter 9: Modern CSS Features | Lesson 14 of 25

CSS At-Rules

Most of CSS is about styling elements, but a handful of special rules -- always starting with an @ symbol -- instead give instructions to the stylesheet itself: what character encoding to expect, how to name an XML namespace, how to scope a block of rules, or how to define custom counter symbols. Think of these at-rules as configuration directives sitting alongside your regular styling rules.
Syntax
css
@media (condition) { }
@import url("file.css");
@font-face { }
@keyframes name { }
@supports (property: value) { }

Declaring File Encoding with @charset

@charset tells the browser which character encoding a stylesheet file uses, which matters when your CSS contains non-ASCII characters like curly quotes or accented letters inside content: strings.

It must be the literal first characters in the file -- not even a comment can come before it -- or the browser silently ignores it.

Note:
  • Save your CSS file itself as UTF-8 and declare @charset "UTF-8"; together, so the declared encoding always matches the file's actual bytes.
  • Accepted values:
  • @charset "UTF-8"; — the only widely supported encoding
  • must be first — nothing may precede it, not even a comment
  • double quotes — required, single quotes are not valid
  • exactly one — only the first is honored
Warning: Any character, comment, or whitespace before @charset in the file causes browsers to ignore the declaration entirely.

Example: Declaring File Encoding with @charset

css
<style>
p { font-size: 1.2rem; }
@charset "UTF-8";
p::before {
  content: "“";
}
</style>
<p>The opening quote before this text comes from CSS.</p>

Naming XML Namespaces with @namespace

@namespace is used mainly in XML-based documents like SVG or XHTML to associate a CSS prefix with an XML namespace URI, letting selectors like svg|circle target elements from a specific namespace only. In plain HTML documents it is rarely needed, since HTML has just one implicit namespace.

Note:
  • Reach for @namespace mainly when styling inline SVG elements that mix custom XML namespaces alongside standard SVG markup.
  • Accepted values:
  • @namespace url(<uri>); — sets the default namespace
  • @namespace <prefix> url(<uri>); — declares a prefix
  • prefix|element — selector using a prefix
  • |element — any namespace
Warning: @namespace must be declared after any @charset or @import rules but before all other style rules, or it will be ignored.

Example: Naming XML Namespaces with @namespace

css
<style>
@namespace svg url(http://www.w3.org/2000/svg);
svg|circle {
  fill: red;
}
</style>
<!-- @namespace svg targets SVG elements; it must precede other rules -->
<svg width="120" height="120"><circle cx="60" cy="60" r="50" /></svg>

Limiting Rule Scope with @scope

@scope wraps a block of CSS rules so their selectors only match elements within a specific DOM subtree, defined by a scoping root and an optional scoping limit.

This solves a long-standing problem: applying a rule only "within this card" without needing to prefix every single selector with a unique class name.

Note:
  • Use @scope for component-style CSS where you want a rule like "p { color: red; }" to apply only inside a specific card, not globally across the page.
  • Accepted values:
  • @scope (<root>) — styles apply inside the root
  • @scope (<root>) to (<limit>) — donut scope with a lower boundary
  • :scope — refers to the scope root
  • proximity — the closer scope wins over specificity ties
Warning: The scoping limit you set (to:) excludes elements from that point downward, which can accidentally exclude nested content you meant to still style.

Example: Limiting Rule Scope with @scope

css
<style>
.card { border: 2px solid #1e6fd9; padding: 8px; }
@scope (.card) {
  p {
    color: red;
  }
}
</style>
<div class="card"><p>Inside @scope: red</p></div>
<p>Outside the scope: unaffected</p>

Animating Entrances with @starting-style

@starting-style defines the "before" values a transition animates from when an element that was previously display: none (or newly added to the DOM) first becomes visible.

Normally CSS transitions cannot animate an element's very first appearance, since there is no prior state to transition from -- @starting-style provides that missing starting point.

Note:
  • Pair @starting-style with transition-behavior: allow-discrete when animating properties like display, so the entrance transition has a defined starting point.
  • Accepted values:
  • @starting-style { ... } — defines the style before the first render
  • transition: opacity 0.3s — animates from the starting style
  • display: none to block — works with allow-discrete transitions
Warning: @starting-style only affects the very first render after a discrete property change (like display: none to block); it does not apply to ordinary hover or class-toggle transitions.

Example: Animating Entrances with @starting-style

css
<style>
.box { padding: 20px; background: #cfe8ff; border: 2px solid #1e6fd9; }
@starting-style {
  .box {
    opacity: 0;
  }
}
.box {
  opacity: 1;
  transition: opacity 0.3s;
}
</style>
<div class="box">Fades in when inserted</div>

Building Custom Counters with @counter-style

@counter-style lets you define an entirely custom counter symbol system beyond the built-in list (decimal, roman, disc, and so on) -- for example, custom bullet glyphs, a specific prefix and suffix, or a repeating custom symbol set.

It gives ordered and unordered lists a level of styling control that list-style-type alone cannot reach.

Note:
  • Give your @counter-style a descriptive name and reuse it across multiple lists via list-style-type for consistent custom bullets sitewide.
  • Accepted values:
  • system: cyclic | numeric | alphabetic | symbolic | additive | fixed | extends — how counters are generated
  • symbols: <symbol> — the markers used
  • suffix: <string> — text after the marker
  • prefix: <string> — text before the marker
  • range: <lower> <upper> | auto — counter range
Warning: @counter-style requires a system descriptor (like cyclic or fixed) to be declared, or the browser will not know how to cycle through your custom symbols.

Example: Building Custom Counters with @counter-style

css
<style>
@counter-style thumbs {
  system: cyclic;
  symbols: "👍";
  suffix: " ";
}
ul {
  list-style: thumbs;
}
</style>
<ul>
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
</ul>
Live Example
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Placing @charset anywhere other than the very first line of the very first stylesheet, where browsers require it to be recognized at all.
  2. Using @scope and assuming it behaves like a CSS class-based namespace, when it actually limits which elements a rule's selectors can match based on DOM position.
  3. Forgetting that @starting-style only applies to a transition when the element is also changing display from none, not on every entrance transition.
Chapter Summary
  • @charset declares a stylesheet's text encoding and must be the very first thing in the file to take effect.
  • @scope limits a block of CSS rules to a specific DOM subtree, and @counter-style defines fully custom counter symbols beyond the built-in list.
  • @starting-style defines the "before" state for CSS transitions that animate an element as it first appears in the DOM.
Browser Support

@charset, @namespace, and @counter-style are broadly supported; @scope and @starting-style are newer additions supported in current Chromium and Firefox releases, with Safari catching up.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.