Skip to content

Rule: font and font-family declarations should always end with sans-serif, serif, or monospace #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/rules/font-stack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Rule: You should always end your font/font-family declarations with sans-serif, serif, or monospace.
*/

/*global CSSLint*/
CSSLint.addRule({

//rule information
id: "font-stack",
name: "Disallow incomplete font stacks",
desc: "Checks if font and font-family declarations end with a sans-serif, serif, or monospace.",
browsers: "All",

//initialization
init: function(parser, reporter){
var rule = this,
whiteList = ["sans-serif", "serif", "monospace"], // "cursive" often equals comic sans and "fantasy" can mean virtually anything
onTheWhitelist,
trim;

onTheWhitelist = function (thing) {
var i;
for (i = whiteList.length; i--;) {
if (whiteList[i] === thing) {
return true;
}
}
return false;
};

trim = function (s) {
return s.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, "$1");
};

//check for use of "font" and "font-size"
parser.addListener("property", function(event){
var parts, lastPart, pieces, lastPiece;
if (event.property.text === "font" || event.property.text === "font-family"){
parts = event.value.parts;
lastPart = parts[parts.length - 1];

// With "font-family: sans serif;" (note the missing dash), the last part would be "serif", which *is* on the whitelist.
// In order to be able to catch this, we have to chop it up manually using slightly different rules.
pieces = event.value.text.split(',');
lastPiece = pieces[pieces.length - 1];

if (!onTheWhitelist(trim(lastPiece))) {
reporter.report("Did not find 'sans-serif', 'serif', or 'monospace' at the end of the " + event.property.text + " declaration.", lastPart.line, lastPart.col, rule);
}
}
});
}

});
45 changes: 45 additions & 0 deletions tests/rules/font-stack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(function(){

/*global YUITest, CSSLint*/
var Assert = YUITest.Assert;

YUITest.TestRunner.add(new YUITest.TestCase({

name: "font-stack Rule Errors",

"omitting the generic family in a font-family declaration should result in a warning": function(){
var result = CSSLint.verify(".foo{font-family: 'nop';}", {"font-stack": 1 });
Assert.areEqual(1, result.messages.length);
Assert.areEqual("warning", result.messages[0].type);
Assert.areEqual("Did not find 'sans-serif', 'serif', or 'monospace' at the end of the font-family declaration.", result.messages[0].message);
},
"omitting the generic family in a font declaration should result in a warning": function(){
result = CSSLint.verify(".foo{font: normal small-caps 120%/120% 'nop';}", {"font-stack": 1 });
Assert.areEqual(1, result.messages.length);
Assert.areEqual("warning", result.messages[0].type);
Assert.areEqual("Did not find 'sans-serif', 'serif', or 'monospace' at the end of the font declaration.", result.messages[0].message);
},

"forgetting the dash in 'sans-serif' in a font-family declaration should result in a warning": function(){
var result = CSSLint.verify(".foo{font-family: 'nop', sans serif;}", {"font-stack": 1 });
Assert.areEqual(1, result.messages.length);
Assert.areEqual("warning", result.messages[0].type);
Assert.areEqual("Did not find 'sans-serif', 'serif', or 'monospace' at the end of the font-family declaration.", result.messages[0].message);
},
"forgetting the dash in 'sans-serif' in a font declaration should result in a warning": function(){
result = CSSLint.verify(".foo{font: normal small-caps 120%/120% 'nop', sans serif;}", {"font-stack": 1 });
Assert.areEqual(1, result.messages.length);
Assert.areEqual("warning", result.messages[0].type);
Assert.areEqual("Did not find 'sans-serif', 'serif', or 'monospace' at the end of the font declaration.", result.messages[0].message);
},

"including the generic family in a font-family declaration should not result in a warning": function(){
var result = CSSLint.verify(".foo{font-family: 'nop', sans-serif;}", {"font-stack": 1 });
Assert.areEqual(0, result.messages.length);
},
"including the generic family in a font declaration should not result in a warning": function(){
result = CSSLint.verify(".foo{font: normal small-caps 120%/120% 'nop', sans-serif;}", {"font-stack": 1 });
Assert.areEqual(0, result.messages.length);
}
}));
})();