HTML Guides for @scope
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
Older builds of the validator reported at-rules like @keyframes and @font-face outside an @scope block in a <style> element in the <body>, but the HTML standard allows them there.
Since October 2026, a <style> element can sit in the <body> as long as its styles can't reach anything earlier in the page. Its style rules have to go inside an @scope block, and it can't use @import. An earlier draft of that change allowed only a few at-rules at the top level of the style sheet, such as @media, @supports and @layer. Builds of the Nu HTML Checker from late September and early October 2026 followed the draft and reported every other at-rule with this message.
The final rule lets at-rules that don't select elements stay outside @scope, at the top level or inside @media. That includes @keyframes, @font-face, @page and @counter-style. Current builds of the validator don't report them, so the code doesn't need to change: validate the page again and the error goes away. @import is still not allowed, and it now has a message of its own.
The names these at-rules define, such as an animation name or a font family, apply to the whole page. The HTML standard suggests using them only in the scoped rules of the same <style> element, and picking names that nothing else on the page uses. That way they can't restyle content the browser has already drawn.
Example
Older builds reported both at-rules in this <style> element. Current builds accept it, because the only style rule is inside @scope:
<div class="card">
<style>
@font-face {
font-family: "Card Sans";
src: url("card-sans.woff2") format("woff2");
}
@keyframes card-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@scope {
p {
font-family: "Card Sans", sans-serif;
animation: card-fade-in 0.3s ease-in;
}
}
</style>
<p>Card content</p>
</div>
A <style> element in the <body> contains a CSS style rule that isn't inside an @scope block.
Since October 2026, the HTML standard allows <style> in the <body> as long as its styles can't reach anything earlier in the page. That means every style rule has to sit inside an @scope block with nothing between @scope and the opening brace. Without a selector, @scope applies to the parent element of the <style> and everything inside it, so p only matches the paragraphs in that parent. A top-level rule like .card p could restyle any paragraph on the page, including ones the browser has already drawn.
Style rules nested in @media, @supports or @container count too. Put those at-rules inside the @scope block, or put an @scope block inside them. At-rules that don't select elements, like @font-face and @keyframes, can stay at the top level. Two other limits apply to a <style> in the <body>: it can't contain @import, and it must be the first child of its parent.
To fix the error, wrap the rules in @scope { } and drop the part of each selector that pointed at the parent. Inside the block, :scope matches the parent element itself. Writing a selector after the keyword, as in @scope (.card), triggers a different error unless that rule is nested in another @scope. If the styles need to apply to the whole page, move the <style> element into the <head>.
Invalid example
<div class="card">
<style>
.card p {
color: darkslategray;
}
</style>
<p>Card content</p>
</div>
Valid example
<div class="card">
<style>
@scope {
:scope {
padding: 1rem;
}
p {
color: darkslategray;
}
}
</style>
<p>Card content</p>
</div>
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries