- Key Takeaways
- CSS Comments
- Pseudo-comments
- Semi-colons
- Inline vs. next-line placement
- Selectors
- Pseudo-comments as targeted malformed-ness
- At-rules
- Pseudo-comments applied to at-rules with body blocks
- Pseudo-comments applied to at-rules without body blocks
- Pre-processors
- Best Practice
- Frequently Asked Questions about Pseudo Comments in CSS
The CSS spec does not mention it, but you can mimic C-style and/or Unix-style line comments in CSS files (with some caveats). Others have written about them before (see in particular, SitePoint’s Web Foundations post covering CSS Comments). The present post examines them in further detail.
Key Takeaways
- CSS officially supports only C-style multiline comments, but pseudo-comments exploit parsing errors to comment out code unintentionally.
- Pseudo-comments can be created by malforming CSS declarations, such as omitting semicolons or using unrecognized property names, causing subsequent code to be ignored.
- Inline and next-line placement of pseudo-comments can affect whether subsequent CSS rules are applied, with inline pseudo-comments potentially nullifying following declarations on the same line.
- Pseudo-comments can also be applied to at-rules, with different behaviors observed depending on whether the at-rule contains a body block or is terminated by a semicolon.
- While pseudo-comments can be useful for debugging, they are less readable and should not replace standard CSS comments in production code.
CSS Comments
CSS parsers, per the spec, officially bless one style for comments, the multi-line comment from C-style languages, which uses a start token, /*
, and an end token, */
, as follows:
/*
characters between, and including, the start and
end tokens are ignored by the parser,
*/
And so a rule declaration in comments will be ignored:
body {
background: red;
/*
background: white;
*/
}
A block declaration in comments will be ignored:
/*
body {
background: red;
}
*/
In each of those examples, we are using the comment syntax intentionally to instruct the parser to ignore the content.
However, we can do that by accident, as with malformed declarations, such as
body {
background: red /* missing semi-colon */
background: blue;
}
In this example, neither background declaration is applied because of the missing semi-colon. The parser scans for the next semi-colon, determines the entire two-line statement is malformed, and so ignores the entire lexed content. The same thing happens if we leave out the property value altogether:
body {
background:
background: blue; /* this declaration is not applied */
}
And that shows that we can use malformed declarations as…
Pseudo-comments
We’ll refer to these as “pseudo-comments” because, properly speaking, these are not comments that terminate at an end-of-line character. Instead they work by malforming the input that follows them, even on subsequent lines. And this is due to the error handling process for Rule sets, declaration blocks, and selectors:
“the whole statement should be ignored if there is an error anywhere in the selector, even though the rest of the selector may look reasonable in CSS 2.1.”
In the following example, taken from the spec, the second ruleset is ignored due to the presence of the invalid “&” character in the selector:
h1, h2 {color: green }
h3, h4 & h5 {color: red } /* <= ignored */
h6 {color: black }
Again, in the following, the second and third declarations are ignored due to the presence of extra characters in the background property name:
body {
background: red;
xbackground: white; /* property name is not recognized */
y background: blue; /* property name is not well-formed */
}
A quick tour around the English language keyboard shows the following special characters will act as single-line declaration comments:
selector {
~ property-name: ignored;
` property-name: ignored;
! property-name: ignored;
@ property-name: ignored;
# property-name: ignored;
$ property-name: ignored;
% property-name: ignored;
^ property-name: ignored;
& property-name: ignored;
* property-name: ignored;
_ property-name: ignored;
- property-name: ignored;
+ property-name: ignored;
= property-name: ignored;
| property-name: ignored;
\ property-name: ignored;
: property-name: ignored;
< property-name: ignored;
. property-name: ignored;
> property-name: ignored;
, property-name: ignored;
? property-name: ignored;
/ property-name: ignored;
}
Rather than use just any character, though, stick with C and Unix convention, and use either #
or //
:
// background: ignored;
# background: ignored;
Semi-colons
Semi-colons are the end tokens of rule declarations. Thus, they cannot “comment” text that follows them. In spec-speak, the parser treats a dangling semi-colon as a malformed declaration (a declaration missing a name, colon, or value).
As shown earlier, when regular multi-line comments are malformed, that is, when start and end tokens are not balanced around a ruleset or declaration, the subsequent declaration or ruleset is ignored by the parser. The following will in effect “comment” out both background declarations because the parser will search for the next end-of-declaration token (the semi-colon) for the affected declaration:
body {
background:
background: blue; /* both lines ignored */
}
That’s fixed by adding a semi-colon after the comment, before the next declaration (thus the background blue declaration will be applied):
body {
background: ; /* ignored */
background: blue; /* processed */
}
The effect is the same with a pseudo-comment on a line missing its semi-colon:
body {
background: # red /* ignored */
background: blue; /* also ignored */
}
and corrected by restoring the semi-colon:
body {
background: # red; /* ignored */
background: blue; /* processed */
}
Inline vs. next-line placement
This is where the “pseudo” enters into the term “pseudo-comment.” It may be reason enough not to call these “comments” at all as they break from the end-of-line convention of C or Unix-style line comments.
A pseudo-comment placed on its own line will suppress a declaration on the next line. In the following, the background will be blue:
body {
//
background: white !important; /* ignored */
background: blue;
}
A pseudo-comment placed after a valid declaration on the same line will suppress a declaration on the next line. In the following, the background will be white rather than blue:
body {
background: white; // next line is ignored...
background: blue !important;
}
Even a “minified” version of a CSS selector with an inline pseudo-comment will behave as a single-declaration comment. In the following, the first background declaration is ignored due to the presence of the comment token, #
, recognized by the parser as terminating at the next semi-colon, and the second background declaration is recognized as well-formed and therefore applied (in this case, blue will be applied to the body background):
body { // background: red !important; background: blue; }
Selectors
The same rule-based behavior applies to selectors.
An entire selector ruleset is ignored when the selector is preceded by a pseudo-comment, whether inline
// body {
background: white !important;
}
or next-line:
//
body {
background: white !important;
}
Pseudo-comments as targeted malformed-ness
Pseudo-comments work by taking advantage of the spec’s Rules for handling parsing errors. In effect, they work by exploiting their malformed-ness.
Unknown values
“User agents must ignore a declaration with an unknown property.”
A declaration containing an unrecognized property name will not be evaluated, as, for example, the comment
property in the following body
ruleset:
body {
comment: 'could be text or a value';
}
Illegal values
“User agents must ignore a declaration with an illegal value.”
The color
property defined below is ignored because the value is a string rather than a value or color keyword:
body {
color: "red";
}
Malformed declarations and statements
“User agents must handle unexpected tokens encountered while parsing a declaration [or statement] by reading until the end of the declaration [or statement], while observing the rules for matching pairs of (), [], {}, “”, and ”, and correctly handling escapes.”
body {
-color: red;
}
Declarations malformed by unmatched pairs of ()
, []
, {}
, ""
, and ''
are more comprehensively ignored (and therefore more dangerous) than others. And the quoting characters ""
, and ''
are processed differently than the grouping characters ()
, []
, {}
.
Quoting characters
The unpaired apostrophe in the second declaration below will prevent the subsequent declaration in the ruleset from being processed (thus, the background will be red):
body {
background: red;
'background: white; /* ignored */
background: blue; /* also ignored */
}
However, a third declaration after the apostrophe will be processed (thus the background will be gold):
body {
background: red;
'background: white; /* ignored */
background: blue; /* also ignored */
background: gold; /* processed */
}
In sum, you can’t terminate a single quoting character on its own line.
Grouping characters
In general, grouping characters ()
, []
, {}
should be avoided as pseudo-comments because they have more drastic effects in that they interfere more extensively with the parser’s block recognition rules, and so will “comment” out more than single declarations. For the sake of completeness, we’ll examine a few of these.
For example, the appearance of unmatched starting group characters suppresses all subsequent declarations to the end of the stylesheet (not just the ruleset). This is true of commas, brackets, and braces.
In the following, only the background: red;
declaration is processed; all declarations and selectors after that in the entire stylesheet will be ignored:
body {
background: red;
{ /* *every* declaration that follows will be ignored,
including all subsequent selectors, to the
end of the stylesheet. */
background: white;
color: aqua;
margin: 5px;
...
}
When grouping characters are matched, the grouped and subsequent ungrouped declarations in the ruleset will be suppressed. In the following, the background will be red, not gold:
body {
background: red;
(
background: white;
background: blue;
background: fuchsia;
)
background: gold;
}
A closing comma or bracket will suppress only the next declaration that appears. In the following, the background will be gold:
body {
background: red;
]
background: white;
background: blue;
}
A closing brace, }
, however, will suppress all declarations to the end of the ruleset. In the following, the background will be red:
body {
background: red;
}
background: white;
background: blue;
}
At-rules
At-rules have two forms:
- a body declaration denoted by braces,
{ ... }
(such as@media
), - a rule declaration closed with a semi-colon
;
(such as@charset
).
Pseudo-comments on body-block at-rules behave the same as for selectors (i.e., the entire at-rule is ignored).
Pseudo-comments applied to at-rules with body blocks
For at-rules containing body blocks, such as @keyframes
, @media
, @page
. and @font-face
, the entire at-rule ruleset is ignored when the at-rule is preceded by a pseudo-comment, whether inline
// @media (min-width: 0) {
body {
background: white !important;
}
}
or next-line:
//
@media (min-width: 0) {
body {
background: white !important;
}
}
Pseudo-comments applied to at-rules without body blocks
At-rules without blocks, such as @charset
and @import
, provide a fascinating exception to inline pseudo-comment behavior.
An at-rule with a pseudo-comment after the keyword will be ignored:
/* the pseudo-comment before url()
suppresses the entire @import */
@import // url('libs/normalize.css');
But a pseudo-comment that precedes an at-rule suppresses both the import and the first rule or selector after the import. This is because the parser treats a pseudo-commented @import
as a malformed statement, and looks for the next matching braces in order to complete the next ruleset.
Thus, a pseudo-comment before one @import
in a series of @import
rules will suppress all subsequent @import
rules and the first declaration or selector after the last import:
// @import url('libs/normalize.css');
/* NONE of these loads because previous statement is
processed as a malformed statement, and the parser
looks for the next matching braces. */
@import url('libs/normalize.css');
@import url('libs/example.css');
@import url('libs/other.css');
@import url('libs/more.css');
@import url('libs/another.css');
@import url('libs/yetmore.css');
The fix for this is surprisingly simple: just add an empty body block after the comment @import
// @import url('libs/normalize.css');
{} /* now, the next import will load */
@import url('libs/normalize.css');
This is fun for debugging, but that behavior is peculiar enough that you should avoid the pseudo-comments approach to at-rules without body blocks, and use the multi-line syntax instead.
At-rules and Unknown at-keywords
“User agents must ignore an invalid at-keyword together with everything following it, up to the end of the block that contains the invalid at-keyword, or up to and including the next semicolon (;), or up to and including the next block ({…}), whichever comes first”
We can illustrate all that by using an unknown at-keyword, @comment
, as a custom at-rule alternative to the multi-line syntax. For example, the following at-rule is parsed to the closing brace, }
, determined to be malformed, and then ignored:
@comment {
I'm not processed in any way.
}
That looks harmless and readable at first, but due to the presence of the apostrophe in I'm
, we’ve reintroduced the quoting character problem (i.e., you can’t terminate the single quoting character on its own line). That means, a subsequent at-rule or selector will also be ignored if our custom @comment
’s body is closed on its own line, because the rule’s declaration is malformed by the presence of the apostrophe in I'm
:
@comment {
I'm not processed in any way. }
body { background: blue; } /* this whole block will not be processed either! */
That can be rescued with outer quotes, either inside the braces
@comment {
"I'm not processed in any way." } /* fixed */
body { background: blue; } /* this block will work */
Or by leaving off the braces and instead terminating the pseudo-comment with a semi-colon, either inline:
@comment "I'm not processed in any way.";
body { background: blue; } /* this works */
or next-line
@comment
"I'm not processed in any way.";
body { background: blue; } /* this works */
Pre-processors
The various CSS pre-processors support similar multiline and single-line comments.
Sass
Sass supports standard multiline CSS comments with /* */, as well as single-line comments with //. The multiline comments are preserved in the CSS output where possible, while the single-line comments are removed.
Compressed mode will normally strip out all comments, unless the comment is preceded by /*!
.
However, you can use a single-character pseudo-comment, such as #
and the output will contain the commented line.
body {
# background: red;
}
Less
Both block-style and inline comments may be used.
It is not clear (to me, at least) whether Less will suppress these comments or print them to the output. From StackOverflow posts, it appears Less will aggregate line-comments at block level.
Stylus
Stylus also supports multiline /* */
and single-line comments //
, but suppresses these from the output if the compress
directive is enabled. If you always want multiline comments to print to the output, use Multi-line buffered comments.
Multi-line comments which are not suppressed start with /*!. This tells Stylus to output the comment regardless of compression.
/*!
* This will appear in the output.
*/
Best Practice
“Readability counts.”
https://www.python.org/dev/peps/pep-0020/‘>Zen of Python
Comments can make obscure code more readable, but readability depends on more than one convention. Pseudo-comments in CSS are less about readability than about playing against convention (aka, the parser).
If you find you need to use pseudo-comments:
- Stick to the C and Unix convention and use either
//
or#
for the pseudo-comment delimiter. - Place pseudo-comments on the same line before the item to be ignored.
- Use whitespace to separate the pseudo-comment delimiter from the intended rule ~ e.g.
# background: ignored;
.
Use pseudo-comments:
- Use pseudo-comments for debugging, notably when using an interactive CSS edit panel, such as Chris Pederick’s Web Developer extension (chrome, firefox, opera).
- Use pseudo-comments to prevent individual declarations, selectors, or at-rules with bodies from being processed.
Avoid pseudo-comments:
- Avoid pseudo-comments for use with textual descriptions and at-rules without bodies (e.g., @import) ~ use multi-line
/* ... */
comments instead. - Avoid the quoting characters
'', ""
~ they are hard for human eyes to scan and cannot be terminated on their own line. - Avoid the grouping characters
(), [], {}
~ they introduce more complicated scanning (and cannot be terminated on their own line). - Avoid pseudo-comments in production code ~ though not “harmful”, they are merely extra bytes at that point.
Frequently Asked Questions about Pseudo Comments in CSS
What are pseudo comments in CSS?
Pseudo comments in CSS are a unique way of writing comments that are not recognized by the browser. Unlike regular comments, which are enclosed within /* and */, pseudo comments are enclosed within /**/ and are not parsed by the browser. This means that they can be used to hide certain parts of your CSS code from the browser, which can be useful for debugging or for creating fallbacks for older browsers.
How do I write a pseudo comment in CSS?
Writing a pseudo comment in CSS is quite simple. All you need to do is enclose your comment within /**/ instead of /* and */. For example, if you wanted to write a pseudo comment saying “This is a pseudo comment”, you would write it as /This is a pseudo comment/. This comment will not be recognized by the browser and will not affect your CSS code.
How do browsers parse CSS styles?
When a browser loads a webpage, it first downloads the HTML and CSS files. The browser then parses the CSS file, reading it from top to bottom and applying the styles as it goes. If it encounters a comment, it will ignore it and move on to the next line of code. However, if it encounters a pseudo comment, it will not recognize it as a comment and will ignore it completely.
Can I use pseudo comments to hide code from certain browsers?
Yes, you can use pseudo comments to hide code from certain browsers. This can be useful if you have code that only works in certain browsers and you want to provide a fallback for other browsers. By placing the code within a pseudo comment, you can ensure that it is ignored by browsers that do not support it.
What happens to CSS when a page is loaded?
When a page is loaded, the browser first downloads the HTML and CSS files. It then parses the CSS file, applying the styles as it goes. If it encounters a comment, it will ignore it and move on to the next line of code. However, if it encounters a pseudo comment, it will not recognize it as a comment and will ignore it completely.
How do I add comments in CSS?
To add a comment in CSS, you simply enclose your comment within /* and */. For example, if you wanted to add a comment saying “This is a comment”, you would write it as /This is a comment/. This comment will be ignored by the browser and will not affect your CSS code.
What is the difference between a comment and a pseudo comment in CSS?
The main difference between a comment and a pseudo comment in CSS is how they are recognized by the browser. A comment is enclosed within /* and */ and is ignored by the browser. A pseudo comment, on the other hand, is enclosed within /**/ and is not recognized by the browser at all.
Can I use pseudo comments to debug my CSS code?
Yes, you can use pseudo comments to debug your CSS code. By placing problematic code within a pseudo comment, you can isolate it from the rest of your code and see how your webpage behaves without it. This can be a useful tool for identifying and fixing issues in your CSS code.
Are pseudo comments supported by all browsers?
Pseudo comments are not recognized by any browser, which is what makes them so useful. Because they are not recognized as comments, they are completely ignored by the browser. This means that you can use them to hide code from the browser, regardless of which browser you are using.
Can I use pseudo comments to create fallbacks for older browsers?
Yes, you can use pseudo comments to create fallbacks for older browsers. By placing code that is not supported by older browsers within a pseudo comment, you can ensure that it is ignored by those browsers. You can then provide alternative code outside of the pseudo comment for those browsers to use.
David Kaye has been a front-end developer since 1999, deeply interested in JavaScript, and (since 2006) test-driven development. He has worked at large firms such as Charles Schwab, The Gap, and Blue Shield of California, as well as smaller startup-sized companies including Glassdoor.