JMM’s

Unorganized notes on CSS

(Constructed CSS, other stuff.)

Programmatic CSS analysis

Programmatic CSS is called a constructed CSSStyleSheet in the CSS Object Model (CSSOM) spec.

Find unused rules

The following snippet tries to find unused rules. It does so by taking the first stylesheet, getting all the CSSStyleRules, then seeing if the selectorText matches anything in the document.

[...document.styleSheets].flatMap(x => [...x.cssRules]).filter(e => e instanceof CSSStyleRule).filter(e => !document.querySelectorAll(e.selectorText).length)

Remove the “!” to find used rules:

[...document.styleSheets].flatMap(x => [...x.cssRules]).filter(e => e instanceof CSSStyleRule).filter(e => document.querySelectorAll(e.selectorText).length)

Issues to fix with it:

  • Some rules will only match occasionally, maybe it’s more interesting to get a list of classes.
  • Probably doesn’t traverse into layers or media queries.
  • Can’t access cross-origin stylesheets

This version just looks at accessible rules:

 [...document.styleSheets].flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}}).filter(e => e instanceof CSSStyleRule).filter(e => document.querySelectorAll(e.selectorText).length)

Find rules with certain properties

Here are some snippets to find CSS rules that declare properties or have properties set to some value:

function cssRulesWithProperty(property) {
    return [...document.styleSheets]
	.flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}})
	.filter(e => e instanceof CSSStyleRule)
	.filter(e => [...e.style].includes(property));
}

function cssRulesWithPropertyValue(property, value) {
    return [...document.styleSheets]
	.flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}})
	.filter(e => e instanceof CSSStyleRule)
	.filter(e => [...e.style].includes(property) && e.style.getPropertyValue(property) == value);
}

You could use this to find CSS rules that make some elements sticky:

cssRulesWithPropertyValue("position", "sticky")

And here’s how you might try to remove that sticky property:

cssRulesWithPropertyValue("position", "sticky").forEach(rule => rule.style.removeProperty("position"))

This doesn’t always work, however (which I should fix).

Find all elements with certain properties

Here’s an example of how to find all position: sticky elements:

[...document.querySelectorAll("*")].filter(el => getComputedStyle(el).getPropertyValue("position") == "sticky")

And here’s how you’d change them all to “static”:

[...document.querySelectorAll("*")].filter(el => getComputedStyle(el).getPropertyValue("position") == "sticky").forEach(el => el.style.position = "static")

I’m not sure if there’s a more efficient way to find matching elements directly.

Listing classes?

Not sure that you can directly list classes without parsing .selectorText yourself. But I don’t think just making a regexp like “\b\.([^. ]+)\b” (or whatever) would work since you can have dots in attribute selectors like “a[data-something*=".something"]”.