parser.addListener("startfontface", function() {
parser.addListener("endstylesheet", function() {
reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
* Rule: You shouldn't need more than 9 font-size declarations.
name: "Disallow too many font sizes",
desc: "Checks the number of font-size declarations.",
url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
init: function(parser, reporter) {
// check for use of "font-size"
parser.addListener("property", function(event) {
if (event.property.toString() === "font-size") {
parser.addListener("endstylesheet", function() {
reporter.stat("font-sizes", count);
reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
* Rule: When using a vendor-prefixed gradient, make sure to use them all.
name: "Require all gradient definitions",
desc: "When using a vendor-prefixed gradient, make sure to use them all.",
url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
init: function(parser, reporter) {
parser.addListener("startrule", function() {
parser.addListener("property", function(event) {
if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
gradients[RegExp.$1] = 1;
} else if (/\-webkit\-gradient/i.test(event.value)) {
parser.addListener("endrule", function(event) {
missing.push("Firefox 3.6+");
missing.push("Webkit (Safari 5+, Chrome)");
if (!gradients.oldWebkit) {
missing.push("Old Webkit (Safari 4+, Chrome)");
missing.push("Opera 11.1+");
if (missing.length && missing.length < 4) {
reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
* Rule: Don't use IDs for selectors.
name: "Disallow IDs in selectors",
desc: "Selectors should not contain IDs.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
init: function(parser, reporter) {
parser.addListener("startrule", function(event) {
var selectors = event.selectors,
for (i=0; i < selectors.length; i++) {
for (j=0; j < selector.parts.length; j++) {
part = selector.parts[j];
if (part.type === parser.SELECTOR_PART_TYPE) {
for (k=0; k < part.modifiers.length; k++) {
modifier = part.modifiers[k];
if (modifier.type === "id") {
reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
} else if (idCount > 1) {
reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
* Rule: IE6-9 supports up to 31 stylesheet import.
* http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
name: "@import limit on IE6-IE9",
desc: "IE6-9 supports up to 31 @import per stylesheet",
browsers: "IE6, IE7, IE8, IE9",
init: function(parser, reporter) {
parser.addListener("startpage", startPage);
parser.addListener("import", function() {
parser.addListener("endstylesheet", function() {
if (count > MAX_IMPORT_COUNT) {
"Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
* Rule: Don't use @import, use <link> instead.
name: "Disallow @import",
desc: "Don't use @import, use <link> instead.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
init: function(parser, reporter) {
parser.addListener("import", function(event) {
reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
* Rule: Make sure !important is not overused, this could lead to specificity
* war. Display a warning on !important declarations, an error if it's
* used more at least 10 times.
name: "Disallow !important",
desc: "Be careful when using !important declaration",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
init: function(parser, reporter) {
// warn that important is used and increment the declaration counter
parser.addListener("property", function(event) {
if (event.important === true) {
reporter.report("Use of !important", event.line, event.col, rule);
// if there are more than 10, show an error
parser.addListener("endstylesheet", function() {
reporter.stat("important", count);
reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
* Rule: Properties should be known (listed in CSS3 specification) or
* be a vendor-prefixed property.
name: "Require use of known properties",
desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
init: function(parser, reporter) {
parser.addListener("property", function(event) {
// the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
reporter.report(event.invalid.message, event.line, event.col, rule);
* Rule: All properties should be in alphabetical order.
id: "order-alphabetical",
name: "Alphabetical order",
desc: "Assure properties are in alphabetical order",
init: function(parser, reporter) {
var startRule = function () {
var endRule = function(event) {
var currentProperties = properties.join(","),
expectedProperties = properties.sort().join(",");
if (currentProperties !== expectedProperties) {
reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
parser.addListener("startrule", startRule);
parser.addListener("startfontface", startRule);
parser.addListener("startpage", startRule);
parser.addListener("startpagemargin", startRule);
parser.addListener("startkeyframerule", startRule);
parser.addListener("startviewport", startRule);
parser.addListener("property", function(event) {
var name = event.property.text,
lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
properties.push(lowerCasePrefixLessName);
parser.addListener("endrule", endRule);
parser.addListener("endfontface", endRule);
parser.addListener("endpage", endRule);
parser.addListener("endpagemargin", endRule);
parser.addListener("endkeyframerule", endRule);
parser.addListener("endviewport", endRule);
* Rule: outline: none or outline: 0 should only be used in a :focus rule
* and only if there are other properties in the same rule.
name: "Disallow outline: none",
desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
init: function(parser, reporter) {
function startRule(event) {
selectors: event.selectors,
if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
} else if (lastRule.propCount === 1) {
reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
parser.addListener("startrule", startRule);
parser.addListener("startfontface", startRule);
parser.addListener("startpage", startRule);
parser.addListener("startpagemargin", startRule);
parser.addListener("startkeyframerule", startRule);
parser.addListener("startviewport", startRule);
parser.addListener("property", function(event) {
var name = event.property.text.toLowerCase(),
if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
parser.addListener("endrule", endRule);
parser.addListener("endfontface", endRule);
parser.addListener("endpage", endRule);
parser.addListener("endpagemargin", endRule);
parser.addListener("endkeyframerule", endRule);
parser.addListener("endviewport", endRule);
* Rule: Don't use classes or IDs with elements (a.foo or a#foo).
id: "overqualified-elements",
name: "Disallow overqualified elements",
desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
init: function(parser, reporter) {
parser.addListener("startrule", function(event) {
var selectors = event.selectors,
for (i=0; i < selectors.length; i++) {
for (j=0; j < selector.parts.length; j++) {
part = selector.parts[j];
if (part.type === parser.SELECTOR_PART_TYPE) {
for (k=0; k < part.modifiers.length; k++) {
modifier = part.modifiers[k];
if (part.elementName && modifier.type === "id") {
reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
} else if (modifier.type === "class") {
if (!classes[modifier]) {