Skip to content

Instantly share code, notes, and snippets.

@rndme
Created December 5, 2025 05:50
Show Gist options
  • Select an option

  • Save rndme/2acd64fafe0ef828c3657311cecbdee8 to your computer and use it in GitHub Desktop.

Select an option

Save rndme/2acd64fafe0ef828c3657311cecbdee8 to your computer and use it in GitHub Desktop.
css parser formatter
// dan's naive css formatter for editors
// reliably parses and formats css using browser's CSS engine:
// sorts properties in rules, removes property repeats (and fallbacks), removes vendor-specific syntax, and removes comments.
// punctuationReplacements are executed while quoted strings are removed, making rule content predictable
////////////////////
// DOES NOT SUPPORT NESTED RULES - MAINLY TO FORMAT SNIPPETS IN A TEXT EDITOR - NOT PRODUCTION MACHINERY!!!!
////////////////////
function formatCSS(strCSS, punctuationReplacements) {
var reps = {}, pr = punctuationReplacements;
function processRuleText(s) { // note: already parsed and cleaned-up by browser at this point
s = s.replace(/\\"/g, "❤️") // kill quotes so we can ignore thier complex processing demands
.replace(/"([^"]+?)"/g, function(j, a) {
var id = "~" + Math.random().toString(36).slice(-8) + "~";
reps[id] = a;
return id;
});
// now there are no comments or quoted strings that could look like rules but aint
s = s.replace(/([;,])\s+/g, "$1"); // space around punctiaion
s = s.replace(/rgba?\(([\d,\.]+)\)/g, function(j, a) { // compress color
var or = a.split(",");
r = or.map(Number).map(x => ("00" + x.toString(16)).slice(-2));
if(r[3]) r[3] = ("00" + Math.floor(or[3] * 255).toString(16)).slice(-2);
if(r[0][0] == r[0][1] && r[1][0] == r[1][1] && r[2][0] == r[2][1] && (!r[3] || r[3][0] == r[3][1])) r = [r[0][0], r[1][0], r[2][0], r[3] || ""];
return "#" + r.join("");
});
s = s.split(/[\{\}]/g); // need upgraded to support nested rules
s[1] = s[1].trim().split(";").sort().filter(String).join(";")+";";
return s[0]+"{"+s[1]+"}";
} //end processRuleText()
function iterateRules(rules) {
var buff = [];
[...rules].forEach(function(a, b, c) {
if(a.cssRules?.length) return buff.push(...iterateRules(a.cssRules));
buff.push(processRuleText(a.cssText));
});
buff = buff.join("").replace(/\s?\{\s?/g, "{");
if(pr) Object.keys(pr).forEach(k => buff = buff.replaceAll(k, pr[k])); // apply user-provide transformation replacements
Object.keys(reps).forEach(k => buff = buff.replace(k, '"' + reps[k] + '"').replace(/❤️/g, "\\\"")); // restore quotes
return buff.trim();
} // end iterateRules()
var st = document.createElement("style");
st.disabled=true;
document.body.appendChild(st);
st.innerHTML = strCSS;
var buff = iterateRules(st.sheet.cssRules);
st.remove();
return buff;
}
/*
// example usage to make fairly compressed css:
formatCSS( strYourCSS );
// example usage to make maximally compressed css:
formatCSS( strYourCSS, {": ":":"});
// example usage to make pretty css:
formatCSS( strYourCSS, {
"{": " {\n\t",
"}": "}\n\n",
";": ";\n\t",
": ": ": ",
"\t}": "}",
",": ", "
});
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment