compatiblePrefixes[prop] = variations;
arrayPush.apply(applyTo, variations);
parser.addListener("startrule", function () {
parser.addListener("startkeyframes", function (event) {
inKeyFrame = event.prefix || true;
parser.addListener("endkeyframes", function () {
parser.addListener("property", function (event) {
var name = event.property;
if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
// e.g., -moz-transform is okay to be alone in @-moz-keyframes
if (!inKeyFrame || typeof inKeyFrame !== "string" ||
name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
parser.addListener("endrule", function () {
if (!properties.length) {
for (i = 0, len = properties.length; i < len; i++) {
for (prop in compatiblePrefixes) {
if (compatiblePrefixes.hasOwnProperty(prop)) {
variations = compatiblePrefixes[prop];
if (CSSLint.Util.indexOf(variations, name.text) > -1) {
if (!propertyGroups[prop]) {
full: variations.slice(0),
if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
propertyGroups[prop].actual.push(name.text);
propertyGroups[prop].actualNodes.push(name);
for (prop in propertyGroups) {
if (propertyGroups.hasOwnProperty(prop)) {
value = propertyGroups[prop];
if (full.length > actual.length) {
for (i = 0, len = full.length; i < len; i++) {
if (CSSLint.Util.indexOf(actual, item) === -1) {
propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
* Rule: Certain properties don't play well with certain display values.
* - float should not be used with inline-block
* - height, width, margin-top, margin-bottom, float should not be used with inline
* - vertical-align should not be used with block
* - margin, float should not be used with table-*
id: "display-property-grouping",
name: "Require properties appropriate for display",
desc: "Certain properties shouldn't be used with certain display property values.",
url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
init: function(parser, reporter) {
var propertiesToCheck = {
function reportProperty(name, display, msg) {
if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
var display = properties.display ? properties.display.value : null;
// height, width, margin-top, margin-bottom, float should not be used with inline
reportProperty("height", display);
reportProperty("width", display);
reportProperty("margin", display);
reportProperty("margin-top", display);
reportProperty("margin-bottom", display);
reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
// vertical-align should not be used with block
reportProperty("vertical-align", display);
// float should not be used with inline-block
reportProperty("float", display);
// margin, float should not be used with table
if (display.indexOf("table-") === 0) {
reportProperty("margin", display);
reportProperty("margin-left", display);
reportProperty("margin-right", display);
reportProperty("margin-top", display);
reportProperty("margin-bottom", display);
reportProperty("float", display);
parser.addListener("startrule", startRule);
parser.addListener("startfontface", startRule);
parser.addListener("startkeyframerule", startRule);
parser.addListener("startpagemargin", startRule);
parser.addListener("startpage", startRule);
parser.addListener("startviewport", startRule);
parser.addListener("property", function(event) {
var name = event.property.text.toLowerCase();
if (propertiesToCheck[name]) {
line: event.property.line,
parser.addListener("endrule", endRule);
parser.addListener("endfontface", endRule);
parser.addListener("endkeyframerule", endRule);
parser.addListener("endpagemargin", endRule);
parser.addListener("endpage", endRule);
parser.addListener("endviewport", endRule);
* Rule: Disallow duplicate background-images (using url).
id: "duplicate-background-images",
name: "Disallow duplicate background images",
desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
init: function(parser, reporter) {
parser.addListener("property", function(event) {
var name = event.property.text,
if (name.match(/background/i)) {
for (i=0, len=value.parts.length; i < len; i++) {
if (value.parts[i].type === "uri") {
if (typeof stack[value.parts[i].uri] === "undefined") {
stack[value.parts[i].uri] = event;
reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
* Rule: Duplicate properties must appear one after the other. If an already-defined
* property appears somewhere else in the rule, then it's likely an error.
id: "duplicate-properties",
name: "Disallow duplicate properties",
desc: "Duplicate properties must appear one after the other.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
init: function(parser, reporter) {
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 property = event.property,
name = property.text.toLowerCase();
if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
properties[name] = event.value.text;
* Rule: Style rules without any properties defined should be removed.
name: "Disallow empty rules",
desc: "Rules without any properties specified should be removed.",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
init: function(parser, reporter) {
parser.addListener("startrule", function() {
parser.addListener("property", function() {
parser.addListener("endrule", function(event) {
var selectors = event.selectors;
reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
* Rule: There should be no syntax errors. (Duh.)
desc: "This rule looks for recoverable syntax errors.",
init: function(parser, reporter) {
parser.addListener("error", function(event) {
reporter.error(event.message, event.line, event.col, rule);
name: "Require fallback colors",
desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
init: function(parser, reporter) {
"border-bottom-color": 1,
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 property = event.property,
name = property.text.toLowerCase(),
parts = event.value.parts,
if (propertiesToCheck[name]) {
if (parts[i].type === "color") {
if ("alpha" in parts[i] || "hue" in parts[i]) {
if (/([^\)]+)\(/.test(parts[i])) {
colorType = RegExp.$1.toUpperCase();
if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
event.colorType = "compat";
* Rule: You shouldn't use more than 10 floats. If you do, there's probably
* room for some abstraction.
name: "Disallow too many floats",
desc: "This rule tests if the float property is used too many times",
url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
init: function(parser, reporter) {
// count how many times "float" is used
parser.addListener("property", function(event) {
if (event.property.text.toLowerCase() === "float" &&
event.value.text.toLowerCase() !== "none") {
parser.addListener("endstylesheet", function() {
reporter.stat("floats", count);
reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
* Rule: Avoid too many @font-face declarations in the same stylesheet.
name: "Don't use too many web fonts",
desc: "Too many different web fonts in the same stylesheet.",
url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
init: function(parser, reporter) {